Finding difficulty in making a model in C# - c#

This question sounds very trivial but I couldn't find it on internet. Let's say I am getting response like following json
{
"status": 1,
"msg": "1 out of 2 Transactions Fetched Successfully",
"transaction_details": {
"f9605b13-c300-4d11-b": {
"mihpayid": "14019310624",
"txnid": "f9605b13-c300-4d11-b",
"mode": "UPI",
"status": "success",
"App_Name": "PhonePe"
},
"546576": {
"mihpayid": "Not Found",
"status": "Not Found"
}
}
}
My problem is How do I make a Model in C# (So that I can deserialize this response)?
I tried this one -
public class TransactionDetails
{
[JsonProperty("txnid")]
public string TransactionId;
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("mode")]
public string Mode { get; set; }
[JsonProperty("mihpayid")]
public string MiPayId { get; set; }
[JsonProperty("amt")]
public string amount { get; set; }
[JsonProperty("App_name")]
public string AppName { get; set; }
}
public class ResponseBody
{
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("msg")]
public string Message { get; set; }
[JsonProperty("transaction_details")]
public List<TransactionDetails> Transactions { get; set; }
}
I know the problem, Problem is "transaction_details" is not a List, It is object of object.
How do I model that, The number of Keys and name of the Keys are not known!!
Please help me!
and sorry if the question is too trivial.

Any time you are dealing with a JSON structure where the keys are not static (i.e. the key is a unique identifier, date/time, etc.) you will need to use a Dictionary. You will still have the benefit of using a strongly-typed value, and you can perform any validation operations on the key to handle different formats as necessary.

Related

Saving JSON to DataTable

I need to save data retrieved from API to a DataTable. JSON which is returned from API can't be deserialized directly to DataTable using this code:
DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
I got an error: Unexpected JSON token when reading DataTable. I read that it's beacuse JSON format is not as it should be. Mine is as follows:
{
"page": 1,
"page_size": 1000,
"items": [
{
"id": "e1b019b9a8bf408c9cb964c29e845104",
"asset_id": "5adb0d87882b4e14b99bde74a967e84c",
"alias": "Concrete Pump Yellow",
"serial_number": "QEQ000123",
"model": {
"name": "Pump C50-HP"
},
"operating_hours": {
"hours": 100,
"unit_driven": true
}
}
]
}
I know I need format like [{..}] but can't find workaround, API returns JSON as above. I can deserialize it using this:
var obj = JsonConvert.DeserializeObject(json);
but how can I now add data to DataTable? I'm looking for a solution for it
What the JsonConvert class does is it materializes your string version of the response into an object. For this to work, your string version has to match the structure of the resulting object or the class needs hints to know how to inflate the object. The runtime is telling you that there is a mismatch and it doesn't know how to resolve it.
There are a few ways to get this done. I prefer an structured approach so I would recommend you create classes to receive the data:
var payload = #"{
""page"": 1,
""page_size"": 1000,
""items"": [
{
""id"": ""e1b019b9a8bf408c9cb964c29e845104"",
""asset_id"": ""5adb0d87882b4e14b99bde74a967e84c"",
""alias"": ""Concrete Pump Yellow"",
""serial_number"": ""QEQ000123"",
""model"": {
""name"": ""Pump C50-HP""
},
""operating_hours"": {
""hours"": 100,
""unit_driven"": true
}
}
]
}";
public class ApiResponse
{
[JsonProperty("page")]
public int Page { get; set; }
[JsonProperty("page_size")]
public int PageSize { get; set; }
[JsonProperty("items")]
public IEnumerable<ApiResponseItem> Items { get; set; }
}
public class ApiResponseItem
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("asset_id")]
public string AssetId { get; set; }
[JsonProperty("alias")]
public string Alias { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[JsonProperty("model")]
public ApiResponseModel Model { get; set; }
[JsonProperty("operating_hours")]
public ApiResponseOperatingHours OperatingHours { get; set; }
}
public class ApiResponseModel
{
[JsonProperty("name")]
public string Name { get; set; }
}
public class ApiResponseOperatingHours
{
[JsonProperty("hours")]
public string Hours { get; set; }
[JsonProperty("unit_driven")]
public bool UnitDriven { get; set; }
}
var response = JsonConvert.DeserializeObject<ApiResponse>(payload);
As you can see, the classes use hint attributes to let the deserializer know about the fields. You can then loop through the response.Items enumerable and consume the items as desired.
UPDATE:
For posterity and at the suggestion of #mason, it's important to point out that there is no need to use a DataTable. A quick inspection of the payload reveals the output is a paged version of set of records so it's not equivalent to a data table.
Your issue here is that the json you're deserializing is not a DataTable, its just an Object.
JsonConvert.DeserializeObject(request, typeof(Object)) -> Where Object would be a defined Class with parameter definitions to deserialize the json to, i.e page, page_size, id etc..
Once in this format its fairly easy to coerce it into a DataTable:
https://learn.microsoft.com/en-us/dotnet/api/system.data.datatable?view=net-6.0
The Classes would look something along the lines of:
public class Items
{
public Guid? Id {get;set;}
public Guid? AssetId {get;set;}
public string alias {get;set;}
public string serial_number {get;set;}
public Model model {get;set;}
public OperatingHours operatingHours {get;set;}
}
public class Model
{
public string Name { get;set;}
}
public class OperatingHours
{
public int Hours {get;set;}
public bool Unit_Driven {get;set;}
}
public class OverallObject
{
public int Page {get;set;}
public int PageSize {get;set;}
public List<Items> AllItems {get;set;}
}

How can I seed an enum by name from a Json file

I'm seeding data into my database on startup.
Problem - I want to seed the "YogaPose" by name, not number, as my YogaPose enum is really long (~150+) and trying to count the number is taking too long.
Question - Is this possible?
Here is what the code looks like to get the json data.
if (context.Poses.Any())
{
var posesData =
File.ReadAllText(path + #"/Data/SeedData/poses.json");
var poses = JsonSerializer.Deserialize<List<Pose>>(posesData);
foreach (var pose in poses)
{
context.Poses.Add(pose);
}
await context.SaveChangesAsync();
}
Here is a sample section from the json file, where you can see I'm using the YogaPose enum number. I want to use the name of the enum!
Attempt 1 - I tried using "YogaPose": "YogaPose.Crane" with a failure
Attempt 2 - I tried using "YogaPose": "Crane" with a failure
{
"YogaPose": 1,
"Description": "A compact arm balance, Crane Pose/Crow Pose, called Bakasana in Sanskrit, encourages toning in the abs and the arms, strengthening in the core, and improves focus in the mind.",
"Level": 2,
"YogaPoseTypes": [{"YogaType": 1}],
"Alias": "Crow",
"Icon": "fa fa-users",
"PreparatoryPoses": [{"YogaPose": 7},{"YogaPose": 44},{"YogaPose": 14},{"YogaPose": 122}],
"FollowUpPoses": [{"YogaPose": 5},{"YogaPose": 7}]
}
Here is the Pose entity
public class Pose : BaseEntity
{
public YogaPose YogaPose { get; set; }
public string Description { get; set; }
public YogaLevel Level { get; set; }
public ICollection<YogaPoseType> YogaPoseTypes { get; set; }
public string Sanskrit { get; set; }
public string Benefit { get; set; }
public string Alias { get; set; }
public string Icon { get; set; }
public ICollection<PreparatoryPose> PreparatoryPoses { get; set; }
public ICollection<FollowUpPose> FollowUpPoses { get; set; }
}
Here is a small section of the YogaPose enum
public enum YogaPose
{
[Display(Name = "Crane")]
Crane,
[Display(Name = "Dolphin")]
Dolphin
}
Yes, it's possible but depends on a json converter you are using (Newstonsoft or System.Text.Json). Please, have a look to the article: JsonConverter attributes.
You also should prepare another json input file with strings instead of numbers in the field "YogaPose", for example:
{
"YogaPose": "Crane",
"Description": "A compact arm balance, Crane Pose/Crow Pose, called Bakasana in Sanskrit, encourages toning in the abs and the arms, strengthening in the core, and improves focus in the mind.",
"Level": 2,
"YogaPoseTypes": [{"YogaType": 1}],
"Alias": "Crow",
"Icon": "fa fa-users",
"PreparatoryPoses": [{"YogaPose": 7},{"YogaPose": 44},{"YogaPose": 14},{"YogaPose": 122}],
"FollowUpPoses": [{"YogaPose": 5},{"YogaPose": 7}]
}

C# Newtonsoft.Json Deserialize only top level?

I have to work with an API which handles error responses like this:
{
"error": {
"code": 3,
"message": "error message"
}
}
And success respones like this:
{
"data": {
"key": "value"
}
}
Error respones will always contain a code (integer) and a message (string), where as success respones can be different a lot ranging from just a few key-value-pairs to many objects and arrays.
I have created classes for every success "data" section and I can parse them successfully. Now I struggle with the simple part to determine if the response I got is actually an error or a success response.
My Idea was to create these classes:
public class APIResponse
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public string Data { get; set; }
}
public class APIResponseError
{
[JsonProperty("code")]
public int Error { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
And to serialize to the class APIResponse. This works only for error responses (kinda obvious) as the data responses are more than just a string which the APIResponse.Data actually is. My idea was to not deserialize the data field and just store it as a string in APIResponse.Data. Then, when I check and see that error is null, I would deserialize the APIResponse.Data property with the correct class. But how can I do this?
You can set type of Data property to JToken:
public class APIResponse
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public JToken Data { get; set; }
}
And deserialize later with ToObject:
myCorrectResponse.Data.ToObject<ExpectedDataType>()
But I highly doubt that you will be sent any data in case of error response so I would recommend making APIResponse generic (where T could be object, array, etc.):
public class APIResponse<T>
{
[JsonProperty("error")]
public APIResponseError Error { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
Which, in case of your example json will be used for example like:
class MyClass
{
[JsonProperty("key")]
public string Key { get; set; }
}
JsonConvert.DeserializeObject<APIResponse<MyClass>>(json);

Creating data model to accept all JSON C# [duplicate]

This question already has answers here:
Deserialize JSON with C#
(10 answers)
Closed 5 years ago.
So I'm creating an endpoint using a data model called Chat that will accept data in this JSON form and store it in the database.
[{
"ID": "123456",
"Chat": [{
"ID": "1",
"Message": "User: that's a nice car Dylan: thanks",
"PostedBy": "Dylan",
"PostedOn": "2018-01-23T18:25:43.511Z"
},
{
"ID": "2",
"Message": "User: that's a really nice car Terry: thanks ",
"PostedBy": "Terry",
"PostedOn": "2018-02-23T18:25:43.511Z"
},
{
"ID": "3",
"Message": "User: that's the best car Roger: thanks",
"PostedBy": "Roger",
"PostedOn": "2018-03-23T18:25:43.511Z"
}
]
}]
This is what I have currently and when I send data to the endpoint it only stores the ID, and nothing else in the database. Any thoughts/guidance is appreciated on how I could alter my model to accept the entirety of the data that is being sent.
public class Chat
{
public string ID { get; set; }
public string message { get; set; }
public string postedBy { get; set; }
public DateTime? postedOn { get; set;}
}
I may be wrong here, but it seems to me like you're using a class that represents a single message-instance (the class Chat) to attempt to store a whole list of Chat-data.
If I'm right, the only reason it actually stores ID is that it by chance happens to have the same name for two different levels in your data; one for the outer list (the whole set - and this is what is stored), and one for each of the inner chat-items.
Try to add this class, and use that instead (or rather in addition, since it actually contains a list of instances of your already existing class Chat):
public class ChatThread
{
public string ID { get; set; }
public IEnumerable<Chat> Chat { get; set; }
}
The ID which is being stored on your object is not the ID from the Chat object, but rather the higher ID definition which is common to your Chat objects.
You're were really close, if we discount the fact that you have not taken into consideration that C# is a case-sensitive language.
The "higher" layer is composed of a String ID, but also of an array of Chat objects, so you should create a Class that holds the definition of these two properties.
public class JsonClass
{
public string ID { get; set; }
public Chat[] Chat { get; set; }
}
public class Chat
{
public string ID { get; set; }
public string Message { get; set; }
public string PostedBy { get; set; }
public DateTime PostedOn { get; set; }
}
Since there exist multiple Chat objects for the JsonClass ID property, you have to make it into a collection of some sort. I chose an array, but you can use other Collection objects, such as a List.

Json.NET JsonConvert.DeserializeObject() return null value

i tried to Deserialize this string :
string _jsonObject = {\"Ad\":{\"Type\":\"Request"\,
\"IdAd\":\"xxx#xxx.com\",
\"Category\":\"cat\",
\"SubCategory\":\"subcat\"},
\"Position\":{\"Latitude\":\"38.255\",
\"Longitude\":\"1.2\",
\"Imei\":\"0123456789\"};
}";
Message _message = JsonConvert.DeserializeObject<Message>(_jsonObject);
Works pretty for "Ad" but not instanciate "Position".
Any idea ?
I forgot to make the properties public. Don't forget to do that...
In the interest of helping others that may be experiencing this issue, or one related to it...
In my case, I had an object with an array of other objects, and one of the reference-type properties on those sub-objects was always null after deserialization. I tried all kinds of things, including downloading the JSON.Net source and stepping through it to find the failure point.
To make a long story short, the problem was, of course, my own. Here is a highly simplified version of my JSON and classes.
JSON
{
"$id": "1",
"RowCount": 10,
"Rows": [{
"$id": 2",
"ItemId": "1",
"ItemName": "Some Item",
"Owner": {
"Name": "John Doe",
"Id": "711D04F5-586F-4FD4-8369-4C00B51DD86F",
// other properties...
},
"OwnerId": "711D04F5-586F-4FD4-8369-4C00B51DD86F"
},
// more rows
]
}
Classes
public class Items
{
public int RowCount { get; set; }
public IEnumerable<Item> Rows { get; set; }
}
public class Item
{
private string ownerId;
public string ItemId { get; set; }
public string ItemName { get; set; }
public Person Owner { get; set; }
public string OwnerId
{
get { return this.ownerId; }
set {
if (value != this.ownerId)
{
this.Owner = null;
}
this.ownerId = value;
}
}
}
public class Person
{
public string Name { get; set; }
public string Id { get; set; }
// other properties
}
What was happening is that, because the Owner property appeared in the JSON prior to the OwnerId property, when the OwnerId property was set, the setter code determined that the current value was not the same as the value being set (since the current value was null), so it set the Owner property to null.
To fix it I also check the value being set against the id of the Owner object as well, and skip setting Owner to null if they are the same.
Admittedly, the cause of my problem may not be the same for everyone, but this is at least a cautionary tale to double-check what is happening when your objects are being initialized during deserialization.
I don't know how you are trying to deserialize, but this should work....
string json = "{\"Ad\":{\"Type\":\"Request\", \"IdAd\":\"xxx#xxx.com\", \"Category\":\"cat\", \"SubCategory\":\"subcat\"},\"Position\":{\"Latitude\":\"38.255\", \"Longitude\":\"1.2\", \"Imei\":\"0123456789\"}}";
var obj = JsonConvert.DeserializeObject<RootObject>(json);
public class Ad
{
public string Type { get; set; }
public string IdAd { get; set; }
public string Category { get; set; }
public string SubCategory { get; set; }
}
public class Position
{
public string Latitude { get; set; }
public string Longitude { get; set; }
public string Imei { get; set; }
}
public class RootObject
{
public Ad Ad { get; set; }
public Position Position { get; set; }
}
In my case, my class properties had internal setters and after setting them public the problem solved.
In my case there is a more subtle error. It is easy to add leading or trailing spaces in the json keys by mistake. When that happens, the key is not recognized and attempting to deserialize it sets the value to null.
For example: {" id": 123}
This id field is not recognized because of the leading space " id". To fix it, fix the json to have instead "id".
Make sure the name of array in JSON matches with property name in your class
Illustrating (Look for "Components"):
JSON:
{
"Components": [
{
"Attribute1": "ABC",
"Attribute2": "XYZ"
}
]
}
Class:
public class MyClass
{
public IList<Component> Components { get; set; }
}
Deserialize:
JsonConvert.DeserializeObject<MyClass>(File.ReadAllText(#"ComponentSet.json"))
My problem was that I was including the class name at the beginning of my JSON string. I had copy-pasted from the serialized output of another class that contained the one I wanted to deserialize and I had purposefully included the class name thinking this was the correct JSON string. Once I removed the class name from my JSON string, it deserialized just fine.
This article was helpful in realizing this: https://social.msdn.microsoft.com/Forums/windowsapps/en-US/4d766a28-ff38-477f-8abf-48ed01f74cd2/jsonconvertdeserializeobjectlttgtjsonstring-returning-all-propertieslttgt-as-null?forum=wpdevelop
I did not see this answer here so I am including it hoping that it helps those who made the same silly mistake as me.
I've never had any issues using Newtonsoft.Json, but decided to go with built in json libraries in latest project. Ended up with null result. Turns out the following will fail:
JSON:
{
"myProperty": "abc"
}
CLASS:
public void MyClass
{
public string MyProperty { get; set; }
}
Why does it fail? "myProperty" in json is camel case (starts with lower case letter), while MyProperty in MyClass starts with upper case letter. If you make both cases the same it works. I tried figuring out how to configure case insensitivity for the entire app, but apparently that's not possible to do, so I went back to Newtonsoft.JSON and the problem went away.
In my case, it was because I did not have a public constructor on my class.
This is what my class originally looked like:
public class TreeGroup
{
public string Name { get; set; }
public SiteGroup Group { get; set; }
public List<TreeMimicObject> Children { get; set; }
public TreeGroup(SiteGroup item)
{
// Notice this constructor takes a SiteGroup object and there
// is no default constructor
}
}
so I changed the class from the above to this:
public class TreeGroup
{
public string Name { get; set; }
public SiteGroup Group { get; set; }
public List<TreeMimicObject> Children { get; set; }
public TreeGroup()
{
// Added this default constructor here!!
}
public TreeGroup(SiteGroup item)
{
// ...
}
}
and it worked!
In my case the problem was deserializeobject return null when try to convert null value from json to int.
public class ItemCalcResModel
{
public int cartId;
}
I solved the problem by enable nullable in project:
#nullable enable
public class ItemCalcResModel
{
public int? cartId;
}

Categories

Resources