nested json c# object deserialization - c#

i have the following json string (jsonString)
[
{
"name":"Fruits",
"references":[
{"stream":{"type":"reference","size":"original",id":"1"}},
],
"arts":[
{"stream":{"type":"art","size":"original","id":"4"}},
{"stream":{"type":"art","size":"medium","id":"9"}},
]
}
]
and the following C# objects
class Item
{
public string Name { get; set; }
public List<Stream> References { get; set; }
public List<Stream> Arts { get; set; }
public Item()
{
}
}
class Stream
{
public string Type { get; set; }
public string Size { get; set; }
public string Id { get; set; }
public Stream()
{
}
}
and the following code
Item item = JsonConvert.DeserializeObject<Item>(jsonString);
when I run the code, it creteas the correct number of references and arts, but each stream has null value (type = null, size = null).
is it posible to do this json.net deserializeobject method or should I manually deserialize ?

EDIT: Okay, ignore the previous answer. The problem is that your arrays (references and arts) contain objects which in turn contain the relevant data. Basically you've got one layer of wrapping too many. For example, this JSON works fine:
[
{
"name":"Fruits",
"references":[
{"Type":"reference","Size":"original","Id":"1"},
],
"arts":[
{"Type":"art","Size":"original","id":"4"},
{"type":"art","size":"medium","id":"9"},
]
}
]
If you can't change the JSON, you may need to introduce a new wrapper type into your object model:
public class StreamWrapper
{
public Stream Stream { get; set; }
}
Then make your Item class have List<StreamWrapper> variables instead of List<Stream>. Does that help?

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;}
}

C# Newtonsoft deserialize JSON array

I'm trying to deserialize an array using Newtonsoft so i can display files from a cloud based server in a listbox but i always end up getting this error no matter what i try:
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: [. Path '[0].priv', line 4, position 15.'
Thisis an example try to deserialize:
[
{
"code": 200,
"priv": [
{
"file": "file.txt",
"ext": "txt",
"size": "104.86"
},
{
"file": "file2.exe",
"ext": "exe",
"size": "173.74"
},
],
"pub": [
{
"file": "file.txt",
"ext": "txt",
"size": "104.86"
},
{
"file": "file2.exe",
"ext": "exe",
"size": "173.74"
}
]
}
]
I tried using a C# Class like this:
public class ListJson
{
[JsonProperty("pub")]
public List List { get; set; }
}
public class List
{
[JsonProperty("file")]
public string File { get; set; }
[JsonProperty("ext")]
public string Ext { get; set; }
[JsonProperty("size")]
public string Size { get; set; }
}
[JsonProperty("priv")]
public List List { get; set; }
}
public class List
{
[JsonProperty("file")]
public string File { get; set; }
[JsonProperty("ext")]
public string Ext { get; set; }
[JsonProperty("size")]
public string Size { get; set; }
}
And deserialize with:
List<list> fetch = Newtonsoft.Json.JsonConvert.DeserializeObject<List<list>>(json);
The correct C# class structure for your JSON is the following:
public class FileEntry
{
public string file { get; set; }
public string ext { get; set; }
public string size { get; set; }
}
public class FileList
{
public int code { get; set; }
public List<FileEntry> priv { get; set; }
public List<FileEntry> pub { get; set; }
}
Deserializing it in this way:
var fetch = JsonConvert.DeserializeObject<FileList[]>(json);
var fileList = fetch.First(); // here we have a single FileList object
As said in the other answer, creating a class called List doesn't automagically turn it into a collection of objects. You need to declare the types to be deserialized from an array a collection type (e.g. List<T>, T[], etc.).
Small tip: when in doubt, use json2csharp.com to generate strongly typed classes from a json string.
At the moment List has a single List instance called priv, which despite the name: doesn't make it a list. To deserialize a JSON array ("priv": [...]), it needs to an array or list-like type, for example List<T> for some T. Presumably a List<FileThing>, if we assume that FileThing is actually the second type called List (you have 2).

xamarin c# deserialize Json containing nested object

I have a webservice that return a Json in this format :
[{
"Route0": {
"RouteID": "AAA",
"RouteDescription": "",
"ReturnCode": "0",
"ReturnError": ""
}
}, {
"Route1": {
"RouteID": "AABCLO",
"RouteDescription": "Antoine Abdo Bachaalani Close",
"ReturnCode": "0",
"ReturnError": ""
}
}]
I need to deserialize it:
I created 2 class:
public class PullRouteDetailsObjectChild
{
public string RouteID { get; set; }
public string RouteDescription { get; set; }
public string ReturnCode { get; set; }
public string ReturnError { get; set; }
}
public class PullRouteDetailsObject
{
public PullRouteDetailsObjectChild Route { get; set; }
}
and I am using this code to deserialize:
List<PullRouteDetailsObject> jsonRoutes =
JsonConvert.DeserializeObject<List<PullRouteDetailsObject>>(jsonresult);
I am able to get a list of 2 PullRouteDetailsObject wich is correct but the child object is always null.
I am sure that I am missing something but can't find what. I need to access child object
Thank you for your help.
You need to use this instead:
var jsonRoutes = JsonConvert.DeserializeObject<List<Dictionary<string, PullRouteDetailsObject>>>(jsonresult);
As you're getting a list of objects with a property which contains your 'PullRouteDetailsObject' (Route0, Route1).

Parse complex JSON: multiple loops vs. classes

I have a Json of type :
{
"JobProcessors": [
{
"JobName": "ArchivalJob",
"IsEnabled": true,
"Batching": {
"BatchSize": 0,
"DegreeOfParallelism": -1
},
"Settings": {
"ArchivalJobCollectionPageSize": 50
}
},
{
"JobName": "AuditLogJob",
"IsEnabled": false,
"Batching": {
"BatchSize": 10,
"DegreeOfParallelism": -1
},
"Settings": {}
}
],
"ScheduledJobs": [
{
"JobName": "RemoteStartClientCommandJob",
"PrimaryAction": {
"ConnectionString": "#JobProcessorsIntegrationSBConnectionStringValue#",
"Settings": {
"LeadTimeInSeconds": "600",
"MaxSrsJobCount": 25
}
},
"ErrorAction": {
"ConnectionString": "#PairedJobProcessorIntegrationSBConnectionStringValue#",
"EntityPath": "remotestartqueue",
"Settings": {
"LeadTimeInSeconds": "600",
"MaxSrsJobCount": 25
}
}
}
]
}
I want to check the "IsEnabled" property for all "JobName" for which come under "JobProcessors" category.
In C# what i Have used till now is :
dynamic parsedJson = JsonConvert.DeserializeObject(reader.GetString(1));
foreach (var item in parsedJson)
{
foreach (var smallitem in item)
{
foreach (var tag in smallitem)
{
if(tag.IsEnabled.toString()=="true"){
Console.WriteLine("true");
}
}
}
}
This is giving me correct result except the fact that it also iterates for "ScheduledJobs" . But the main issue is :
Is this the right or most efficient way to do this ? If possible suggest some better method .
One that i know of is using classes , but i may not know the json structure beforehand. Also the json is very huge so making classes can be cumbersome !!
Given that you are already doing JObject.Parse(jsonstring); to parse your JSON string, you can use SelectTokens() with a JSONPath query to find all "JobName" objects under "JobProcessors":
// I want to check the "IsEnabled" property for all "JobName" for which come under "JobProcessors"
foreach (var job in root.SelectTokens("..JobProcessors[?(#.JobName)]"))
{
var isEnabled = (bool?)job["IsEnabled"];
Debug.WriteLine(string.Format("Job {0}: IsEnabled={1}", job["JobName"], isEnabled));
}
Notes:
.. is the recursive descent operator: it recursively descends the JToken hierarchy returning each item, subsequently to be matched against the remaining parts of the query string.
JobProcessors returns values of properties of that name.
[?(#.JobName)] returns array items (of JobProcessors in this case) that are objects with a JobName property.
(bool?) casts the value of "IsEnabled" to a boolean or null if missing.
And the output of this is:
Job ArchivalJob: IsEnabled=True
Job AuditLogJob: IsEnabled=False
As in your code snippet we are using two foreach it may take time for large object. So we can do the same thing in a single foreach or if you have some specific node to fetch or search we can use linq, and for this first we need to convert our json object into c# object. For converting Json object to C# you can use this site "http://json2csharp.com/" then we can Deserialize Json object into c#.
It will be something like this
string jsonString = "your Json Object as string";
var jsonObject = JsonConvert.DeserializeObject<RootObject>(jsonString);
foreach (JobProcessor obj in jsonObject.JobProcessors)
{
string JobName = obj.JobName;
bool value=obj.IsEnabled;
}
And I also converted given Json in c# object if the Json object is same you can directly use these classes.
public class Batching
{
public int BatchSize { get; set; }
public int DegreeOfParallelism { get; set; }
}
public class Settings
{
public int ArchivalJobCollectionPageSize { get; set; }
}
public class JobProcessor
{
public string JobName { get; set; }
public bool IsEnabled { get; set; }
public Batching Batching { get; set; }
public Settings Settings { get; set; }
}
public class Settings2
{
public string LeadTimeInSeconds { get; set; }
public int MaxSrsJobCount { get; set; }
}
public class PrimaryAction
{
public string ConnectionString { get; set; }
public Settings2 Settings { get; set; }
}
public class Settings3
{
public string LeadTimeInSeconds { get; set; }
public int MaxSrsJobCount { get; set; }
}
public class ErrorAction
{
public string ConnectionString { get; set; }
public string EntityPath { get; set; }
public Settings3 Settings { get; set; }
}
public class ScheduledJob
{
public string JobName { get; set; }
public PrimaryAction PrimaryAction { get; set; }
public ErrorAction ErrorAction { get; set; }
}
public class RootObject
{
public List<JobProcessor> JobProcessors { get; set; }
public List<ScheduledJob> ScheduledJobs { get; set; }
}
Hope this will help.
Thank you

Runtime Binder Exception

After a deserialization I save the content in an object:
var obj = JsonConvert.DeserializeObject<dynamic>(responseText);
so I execute a loop for populate a DataGrid
foreach(var item in obj)
{
MainWindow.AppWindow.Squadre_DataGrid.Items.Add(new Teams.Club_Information
{
code = item.code,
name = item.name,
shortName = item.shortName,
squadMarketValue = item.squadMarketValue
});
}
The problem's that inside the foreach the compiler show Runtime Binder Exception.
Why happean this?
Some more details:
Class structure
public class Self
{
public string href { get; set; }
}
public class Fixtures
{
public string href { get; set; }
}
public class Players
{
public string href { get; set; }
}
public class Links
{
public Self self { get; set; }
public Fixtures fixtures { get; set; }
public Players players { get; set; }
}
public class RootObject
{
public Links _links { get; set; }
public string name { get; set; }
public string code { get; set; }
public string shortName { get; set; }
public string squadMarketValue { get; set; }
public string crestUrl { get; set; }
}
JSON structure:
{
"_links": {
"self": { "href": "http://api.football-data.org/alpha/teams/19" },
"fixtures": { "href": "http://api.football-data.org/alpha/teams/19/fixtures" },
"players": { "href": "http://api.football-data.org/alpha/teams/19/players" }
},
"name": "Eintracht Frankfurt",
"code": "SGE",
"shortName": "Eintr. Frankfurt",
"squadMarketValue": "75.475.000 ?",
"crestUrl": "http://upload.wikimedia.org/wikipedia/commons/0/04/Eintracht_Frankfurt_Logo.svg"
}
The object you are deserializing does not contain a property named code. So the line code = item.code causes an exception at runtime, because the Json.Net object behind the dynamic does not contain a value named code.
This means that the Json that you are parsing does not contain a property named code. Or else it only sometimes contains a property named code. In that case you'll have to either parse it is a JObject and check if the property exists or create an type do deserialize it into.'
Edit
Based on the Json that you posted along with the class structure it looks like you should just be deserializing directly into a RootObject class:
var obj = JsonConvert.DeserializeObject<RootObject>(responseText);
Or in any case you can still deserialize into a dynamic but you need to get rid of the foreach since you don't have a collection of RootObject
var obj = JsonConvert.DeserializeObject<dynamic>(responseText);
MainWindow.AppWindow.Squadre_DataGrid.Items.Add(new Teams.Club_Information
{
code = obj.code,
name = obj.name,
shortName = obj.shortName,
squadMarketValue = obj.squadMarketValue
});
Where you went wrong was the foreach. Since obj is dynamic there is no compiler error and the Json.Net JObject that is returned supports iteraction. But the that gives you back each of the property values, (e.g. _links, name, etc) on at a time, not the object that you are interested in.

Categories

Resources