Good Morning, I'm falling on a problem that apparently should be easy but I cannot find a solution.
I have already red this and this posts, nothing helped.
I have an API, that returns data in JSON format with this schema, it has an indexer (I know it's idiotic, I didn't develop it) of the result before each object:
{
"total":2,
"0":{
"id_property":5028080,
"id_company":11719097,
....
},
"1":{
"id_property":4996958,
"id_company":11719097,
....
},
"status":"success"
}
I'm trying to deserialize it with System.Text.Json but it fails also with Newtonsoft.
According to the previous links I made a C# schema like this:
public class RootDTO
{
public int total { get; set; }
public Dictionary<string, PropertyDTO> Property { get; set; }
public string status { get; set; }
}
public class PropertyDTO
{
public int id { get; set; }
public string url { get; set; }
// OTHER PROPERTIES ...
}
I tried to deserialize it with the root Object and directly with the Dictionary in this way, either way it is failing. Null property in the first case, exception in the second case since it finds a "total" property not matching the schema.
RootDTO result = JsonSerializer.Deserialize<RootDTO>(content);
Dictionary<string, PropertyDTO> result = JsonSerializer.Deserialize<Dictionary<string, PropertyDTO>>(content);
Any idea on how to solve this apparently simple problem?
Many thanks everybody
Another thing I wasn't aware is that inside I have a SubObject with the same idiotic format
....
"galleries": [
{
"id": 4441310,
"0": {
"id": 146843541,
"url": "xxx",
"description": "",
"filename": "83732120220325094904.jpg",
"position": 1,
"url_big": "yyy",
"url_original": "kkk"
},
"1": {
"id": 146843542,
"url": "xxx",
"description": "",
"filename": "83732220220325094904.jpg",
"position": 2,
"url_big": "yyy",
"url_original": "kkk"
}
....
}
....
The issue there is that the objects are not represented by properties of the target type RootDTO.
You can handle that using the concept of overflow JSON: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-handle-overflow?pivots=dotnet-6-0
public class RootDTO
{
public int total { get; set; }
public string status { get; set; }
//all the "overflowing" properties will be here
[System.Text.Json.Serialization.JsonExtensionData]
public Dictionary<string, JsonElement> Properties { get; set; }
}
RootDTO result = System.Text.Json.JsonSerializer.Deserialize<RootDTO>(json);
You can later deserialize the JsonElement in your PropertyDTO type.
You can get the values using a method like this where we deserialize to a dict of string keys and take any object so the code won't complain about the various types we have. Then further break it down into the specific fields you need.
EDIT - Try the code here
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class Program
{
public static void Main()
{
var json = #"{
""total"": 2,
""0"": {
""id_property"": 5028080,
""id_company"": 11719097
},
""1"": {
""id_property"": 4996958,
""id_company"": 11719097
},
""status"": ""success""
}";
var data = JsonSerializer.Deserialize<Dictionary<string,object>>(json);
foreach (var obj in data.Where(t => t.Key != "total" && t.Key != "status")) {
var dataObj = JsonSerializer.Deserialize<PropertyDTO>(obj.Value.ToString());
Console.WriteLine(dataObj.id_property);
Console.WriteLine(dataObj.id_company);
}
}
}
public class PropertyDTO
{
public int id_property { get; set; }
public int id_company { get; set; }
// OTHER PROPERTIES ...
}
the easiest way would be to use Newtonsoft.Json. You can try to use JsonExtensionData , but in this case you will have only a generic < string, object > dictionary. This code creates a typed dictionary
using Newtonsoft.Json;
var jsonParsed = JObject.Parse(json);
RootDTO rootDto = new RootDTO
{
total = (int)jsonParsed["total"],
properties = jsonParsed.Properties().Where(p => p.Value.Type is JTokenType.Object)
.ToDictionary(p =>p.Name, p=> p.Value.ToObject<PropertyDTO>());
status = (string)jsonParsed["status"]
}
classes
public class RootDTO
{
public int total { get; set; }
public Dictionary<string, PropertyDTO> properties { get; set; }
public string status { get; set; }
}
public class PropertyDTO
{
[JsonProperty("id_property")]
public int properyId { get; set; }
[JsonProperty("id_company")]
public int companyId { get; set; }
}
A work around can be the following:
Create another class that wraps desired values
public class IdioticWrapper<TValue> : IDictionary<string, JsonElement>
{
private IDictionary<string, TValue> desiredValues
= new Dictionary<string, TValue>();
private IDictionary<string, JsonElement> properties
= new Dictionary<string, JsonElement>();
public JsonElement this[string key]
{
get => this.properties[key];
set
{
// TODO: some checks like is null can deserialize ...
this.desiredValues[key] = JsonSerializer.Deserialize<TValue>(value.GetRawText());
}
}
// There are desired models
public IEnumerable<TValue> DesiredValues
=> this.desiredValues.Values;
public ICollection<string> Keys => this.properties.Keys;
public ICollection<JsonElement> Values => this.properties.Values;
public int Count => this.properties.Count;
public bool IsReadOnly => this.properties.IsReadOnly;
public void Add(string key, JsonElement value) => this.properties.Add(key, value);
public void Add(KeyValuePair<string, JsonElement> item) => this.properties.Add(item);
public void Clear() => this.properties.Clear();
public bool Contains(KeyValuePair<string, JsonElement> item) => properties.Contains(item);
public bool ContainsKey(string key) => this.properties.ContainsKey(key);
public void CopyTo(KeyValuePair<string, JsonElement>[] array, int arrayIndex) => this.properties.CopyTo(array, arrayIndex);
public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator() => this.properties.GetEnumerator();
public bool Remove(string key) => this.properties.Remove(key);
public bool Remove(KeyValuePair<string, JsonElement> item) => this.properties.Remove(item);
public bool TryGetValue(string key, [MaybeNullWhen(false)] out JsonElement value) => this.properties.TryGetValue(key, out value);
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
2 use that wraper wehenever you need to wrap nested data of that kind
public class PropertyDTO
{
public int id_property { get; set; }
public int id_company { get; set; }
[JsonExtensionData]
public IdioticWrapper<object> properties { get; set; }
}
public class RootDTO
{
public int total { get; set; }
public string status { get; set; }
[JsonExtensionData]
public IdioticWrapper<PropertyDTO> properties { get; set; }
public IEnumerable<PropertyDTO> Values
=> this.properties.DesiredValues;
}
and at the end you shold get the correct result
var result = JsonSerializer.Deserialize<RootDTO>(jsonContent);
Console.WriteLine(result.total);
Console.WriteLine(result.status);
var values = result.Values;
// ... and so on
Related
I have a below json, I want to loop the items inside the attribute CheckingUrls.
{
"Root": {
"Urls": {
"CheckingUrls": [
{
"API Management": {
"url": ".azure-api.net",
"displayurl": "*.azure-api.net"
},
"Log Analytics": {
"url": "1.ods.opinsights.azure.com",
"displayurl": "*.ods.opinsights.azure.com"
}
}
]
}
}
}
Here are the C# class
public class Root
{
Urls Urls { get; set; }
}
public class Urls
{
public List<CheckingUrls> CheckingUrls { get; set; }
}
public class CheckingUrls
{
[JsonProperty("API Management")]
public UrlDetails APIManagement { get; set; }
[JsonProperty("Log Analytics")]
public UrlDetails LogAnalytics { get; set; }
}
public class UrlDetails
{
[JsonProperty("url")]
public string url { get; set; }
[JsonProperty("displayurl")]
public string displayurl { get; set; }
}
I am trying to convert it into c# object using the below code
var content = File.ReadAllText(jsonstring);
var settings = JsonConvert.DeserializeObject<Root>(content);
I am getting APIManagement and LogAnalytics as properties in the result. Is it possible to get these as List, so that I can loop the contents without hardcoding the properties in code.
Why I need solution: We might add new child to CheckingUrls and we do not want to change the c# code everytime when we change JSON.
Use a C# Dictionary when you want to convert a JSON Object to C# when you don't have a concrete type. CheckingUrls is already an array, so you end up with
public List<Dictionary<string, UrlDetails>> CheckingUrls { get; set; }
The key of a Dictionary entry is the property name in the array element (like "API Management"), and the value is the object that contains the url and displayurl properties.
This eliminates the need for the CheckingUrls C# class.
If you want a List, you can create a CheckingUrls class
List<CheckingUrls> checkingUrlsList = JObject.Parse(json)
.SelectToken("Root.Urls.CheckingUrls")
.SelectMany(jo => ((JObject)jo).Properties()
.Select(p => new CheckingUrls
{
UrlName = p.Name,
UrlDetails = new UrlDetails
{
url = (string)p.Value["url"],
displayurl = (string)p.Value["displayurl"]
}
}
)).ToList();
public class CheckingUrls
{
public string UrlName { get; set; }
public UrlDetails UrlDetails { get; set; }
}
public class UrlDetails
{
public string url { get; set; }
public string displayurl { get; set; }
}
output ( in a json format)
[
{
"UrlName": "API Management",
"UrlDetails": {
"url": ".azure-api.net",
"displayurl": "*.azure-api.net"
}
},
{
"UrlName": "Log Analytics",
"UrlDetails": {
"url": "1.ods.opinsights.azure.com",
"displayurl": "*.ods.opinsights.azure.com"
}
}
]
but if you changed your mind to a Dictionary
Dictionary<string, UrlDetails> checkingUrlsDict = JObject.Parse(json)
.SelectToken("Root.Urls.CheckingUrls")
.Select(jo => jo.ToObject<Dictionary<string, UrlDetails>>())
.FirstOrDefault();
Given the following json:
[ {"id":"123", ... "data":[{"key1":"val1"}, {"key2":"val2"}], ...}, ... ]
that is part of a bigger tree, how can I deserialize the "data" property into:
List<MyCustomClass> Data { get; set; }
or
List<KeyValuePair> Data { get; set; }
or
Dictionary<string, string> Data { get; set; }
using Json.NET? Either version will do (I prefer List of MyCustomClass though). I already have a class that contains other properties, like this:
public class SomeData
{
[JsonProperty("_id")]
public string Id { get; set; }
...
public List<MyCustomClass> Data { get; set; }
}
where "MyCustomClass" would include just two properties (Key and Value). I noticed there is a KeyValuePairConverter class that sounds like it would do what I need, but I wasn't able to find an example on how to use it. Thanks.
The simplest way is deserialize array of key-value pairs to IDictionary<string, string>:
public class SomeData
{
public string Id { get; set; }
public IEnumerable<IDictionary<string, string>> Data { get; set; }
}
private static void Main(string[] args)
{
var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
var obj = JsonConvert.DeserializeObject<SomeData>(json);
}
But if you need deserialize that to your own class, it can be looks like that:
public class SomeData2
{
public string Id { get; set; }
public List<SomeDataPair> Data { get; set; }
}
public class SomeDataPair
{
public string Key { get; set; }
public string Value { get; set; }
}
private static void Main(string[] args)
{
var json = "{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }";
var rawObj = JObject.Parse(json);
var obj2 = new SomeData2
{
Id = (string)rawObj["id"],
Data = new List<SomeDataPair>()
};
foreach (var item in rawObj["data"])
{
foreach (var prop in item)
{
var property = prop as JProperty;
if (property != null)
{
obj2.Data.Add(new SomeDataPair() { Key = property.Name, Value = property.Value.ToString() });
}
}
}
}
See that I khow that Value is string and i call ToString() method, there can be another complex class.
Thanks #Boo for your answer but in my case I needed to take some small adjustements.
This is how my JSON looks like:
{
"rates": {
"CAD": 1.5649,
"CZK": 26.118,
...
},
"base": "EUR",
"date": "2020-08-16"
}
And my DTO looks like the following:
public IDictionary<string, decimal> Rates { get; set; }
public string Base { get; set; }
public DateTime Date { get; set; }
So the only adjustement was to remove the IEnumerable around the IDictionary.
I ended up doing this:
[JsonConverter(typeof(MyCustomClassConverter))]
public class MyCustomClass
{
internal class MyCustomClassConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
foreach (var prop in jObject)
{
return new MyCustomClass { Key = prop.Key, Value = prop.Value.ToString() };
}
return null;
}
public override bool CanConvert(Type objectType)
{
return typeof(MyCustomClass).IsAssignableFrom(objectType);
}
}
public string Key { get; set; }
public string Value { get; set; }
}
public class Datum
{
public string key1 { get; set; }
public string key2 { get; set; }
}
public class RootObject
{
public string id { get; set; }
public List<Datum> data { get; set; }
}
i used this wizard as well json2csharp.com to generate class for deserialized
for using that
using RestSharp;
using Newtonsoft.Json;
IRestResponse restSharp= callRestGetMethodby_restSharp(api_server_url);
string jsonString= restSharp.Content;
RootObject rootObj= JsonConvert.DeserializeObject<RootObject>(jsonString);
return Json(rootObj);
if you call rest by restsharp
public IRestResponse callRestGetMethodby_restSharp(string API_URL)
{
var client = new RestSharp.RestClient(API_URL);
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("cache-control", "no-cache");
IRestResponse response = client.Execute(request);
return response;
}
also you can get this 6 line of restsharp from getpostman.com tools
You can use public IEnumerable<IDictionary<string, string>> Data as the most answers are recommending, but it is not the best idea, since it creates a new dictionary for each array key value item. I recommend to use List<KeyValuePair<string, string>> instead ( or you can create a custom class as well instead of using KeyValuePair )
var json = "[{ \"id\": \"123\", \"data\": [ { \"key1\": \"val1\" }, { \"key2\" : \"val2\" } ] }]";
List<SomeData> listSomeData = JsonConvert.DeserializeObject<List<SomeData>>(json);
public class SomeData
{
[JsonProperty("id")]
public string Id { get; set; }
public List<KeyValuePair<string, string>> Data { get; set; }
[JsonConstructor]
public SomeData(JArray data)
{
Data = data.Select(d => new KeyValuePair<string, string>(
((JObject)d).Properties().First().Name,
((JObject)d).Properties().First().Value.ToString()))
.ToList();
}
}
Is there anything terribly inefficient here? It seems like this process is taking way longer than it should. I am parsing many JSON files each with a JsonArray of objects. Maybe someone with more experience could point out an error in this method of parsing the JSON into objects, thereby saving me a ton of time.
Also, memory usage slowly creeps upwards MB by MB sometimes causing outofmemoryexceptions..
public void Parse(){
using (BabysFirstsUsersDataEntities db = new BabysFirstsUsersDataEntities()
{
foreach (var path in Directory.EnumerateFiles(#"C:\\examplepath\").OrderBy(f => f))
{
string jsonString = System.IO.File.ReadAllText(path);
JToken tok = JObject.Parse(jsonString);
Debug.WriteLine("path: " + path);
foreach (var x in tok.First.First)
{
JsonUserImageDTO jdto = x.ToObject<JsonUserImageDTO>();
UserImageList uil = jdto.ToDataModel();
if (uil.REID != null)
db.UserImageLists.Add(uil);
}
}
db.SaveChanges();
}
}
An example of what one of the JSON strings in each .json file looks like below. Note that there are around 1000 of these files and each can have thousands of such entries:
{
"results": [
{
"ACL": {
"asdf": {
"read": true,
"write": true
},
"role:admin": { "read": true }
},
"REID": "exampleID",
"createdAt": "datetime-string",
"email": "example",
"objectId": "example",
"updatedAt": "datetimestring",
"urlCount": 1,
"urlList": [ "exampleurl" ]
},
{
"ACL": {
"asdf": {
"read": true,
"write": true
},
"role:admin": { "read": true }
},
"REID": "exampleID",
"createdAt": "datetime-string",
"email": "example",
"objectId": "example",
"updatedAt": "datetimestring",
"urlCount": 1,
"urlList": [ "exampleurl" ]
}
]
}
It looks like there could be several places that could causing the slowness.
Deserialzing JSON
Transforming the object twice (jdto, then uil)
Saving to the database
It may be worth profiling the code to find out exactly what part is taking longer than you'd expect. That said there are some things you can do to generally improve this code.
Deserialize from a steam instead of a string. The way you have it, you basically have the object in memory twice-- once as a string, then once as tok. See the second example in the docs for how to use a stream. Actually, in your case you the same information in memory 4 times -- the string, tok, jdto, and uil. Which brings me to the next point..
Try to eliminate some of the intermediate representations of your object. Generally, the more objects you have laying around, the more time you will spend waiting on the GC.
Move the filtering on the path name to the part where you call EnumerateFiles(). There is no sense in deserializing a file if you are not going to do anything with it.
Have you actually profiled your code? See Erik Lippert's performance rant: Use a profiler or other analysis tool to determine empirically where the bottleneck is before you start investigating alternatives. For instance, you actual performance problem may be somewhere in the BabysFirstsUsersDataEntities db class.
That being said, my immediate reaction is that you have too many intermediate representations of your data, the construction, population and garbage collection of which all take time. These include:
The jsonString which may be large enough to go on the large object heap, and thus permanently impair the performance and memory use of your process.
The JToken tok representation of your entire JSON hierarchy.
Each individual JsonUserImageDTO.
What I would suggest is to eliminate as many of these intermediate representations as possible. As suggested in the documentation you should load directly from a stream rather that loading to a string and parsing that string.
You can also eliminate the JToken tok by populating your data model directly. Let's say your BabysFirstsUsersDataEntities looks like this (I'm just guessing here):
public class BabysFirstsUsersDataEntities
{
public BabysFirstsUsersDataEntities() { this.UserImageLists = new List<UserImageList>(); }
public List<UserImageList> UserImageLists { get; set; }
}
public class UserImageList
{
public string email { get; set; }
public List<string> urlList;
}
And your DTO model looks something like this model provided by http://json2csharp.com/:
public class RootObjectDTO
{
public ICollection<JsonUserImageDTO> results { get; set; }
}
public class JsonUserImageDTO
{
public ACL ACL { get; set; }
public string REID { get; set; }
public string createdAt { get; set; }
public string email { get; set; }
public string objectId { get; set; }
public string updatedAt { get; set; }
public int urlCount { get; set; }
public List<string> urlList { get; set; }
public UserImageList ToDataModel()
{
return new UserImageList { email = email, urlList = urlList };
}
}
public class Asdf
{
public bool read { get; set; }
public bool write { get; set; }
}
public class RoleAdmin
{
public bool read { get; set; }
}
public class ACL
{
public Asdf asdf { get; set; }
[JsonProperty("role:admin")]
public RoleAdmin RoleAdmin { get; set; }
}
Then create the following generic ConvertingCollection<TIn, TOut> utility class:
public class ConvertingCollection<TIn, TOut> : BaseConvertingCollection<TIn, TOut, ICollection<TIn>>
{
readonly Func<TOut, TIn> toInner;
public ConvertingCollection(Func<ICollection<TIn>> getCollection, Func<TIn, TOut> toOuter, Func<TOut, TIn> toInner)
: base(getCollection, toOuter)
{
if (toInner == null)
throw new ArgumentNullException();
this.toInner = toInner;
}
protected TIn ToInner(TOut outer) { return toInner(outer); }
public override void Add(TOut item)
{
Collection.Add(ToInner(item));
}
public override void Clear()
{
Collection.Clear();
}
public override bool IsReadOnly { get { return Collection.IsReadOnly; } }
public override bool Remove(TOut item)
{
return Collection.Remove(ToInner(item));
}
public override bool Contains(TOut item)
{
return Collection.Contains(ToInner(item));
}
}
public abstract class BaseConvertingCollection<TIn, TOut, TCollection> : ICollection<TOut>
where TCollection : ICollection<TIn>
{
readonly Func<TCollection> getCollection;
readonly Func<TIn, TOut> toOuter;
public BaseConvertingCollection(Func<TCollection> getCollection, Func<TIn, TOut> toOuter)
{
if (getCollection == null || toOuter == null)
throw new ArgumentNullException();
this.getCollection = getCollection;
this.toOuter = toOuter;
}
protected TCollection Collection { get { return getCollection(); } }
protected TOut ToOuter(TIn inner) { return toOuter(inner); }
#region ICollection<TOut> Members
public abstract void Add(TOut item);
public abstract void Clear();
public virtual bool Contains(TOut item)
{
var comparer = EqualityComparer<TOut>.Default;
foreach (var member in Collection)
if (comparer.Equals(item, ToOuter(member)))
return true;
return false;
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count { get { return Collection.Count; } }
public abstract bool IsReadOnly { get; }
public abstract bool Remove(TOut item);
#endregion
#region IEnumerable<TOut> Members
public IEnumerator<TOut> GetEnumerator()
{
foreach (var item in Collection)
yield return ToOuter(item);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
You can now populate your db directly as follows:
var rootDTO = new RootObjectDTO
{
results = new ConvertingCollection<UserImageList, JsonUserImageDTO>(() => db.UserImageLists, (x) => { throw new NotImplementedException(); }, (x) => x.ToDataModel())
};
using (var stream = File.Open(path, FileMode.Open))
using (var reader = new StreamReader(stream))
{
JsonSerializer.CreateDefault().Populate(reader, rootDTO);
}
By populating a preallocated rootDTO and ConvertingCollection<UserImageList, JsonUserImageDTO>, your db.UserImageLists will get populated with the contents of the JSON with fewer intermediate representations.
You can create the objects and then deserialize them.
Example:
JsonConvert.DeserializeObject<RootObject>(jsonString);
public class Asdf
{
public bool read { get; set; }
public bool write { get; set; }
}
public class RoleAdmin
{
public bool read { get; set; }
}
public class ACL
{
public Asdf asdf { get; set; }
public RoleAdmin { get; set; }
}
public class Result
{
public ACL ACL { get; set; }
public string REID { get; set; }
public string createdAt { get; set; }
public string email { get; set; }
public string objectId { get; set; }
public string updatedAt { get; set; }
public int urlCount { get; set; }
public List<string> urlList { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
I am new for json , as i am getting following json string.
i need following output 1,2,6,3,5,4 (get id object with comma separated) in asp.net with c#
[
{
"id":1,
"children":
[
{
"id":2,
"children" :
[
{"id":6},
{"id":3},
]
}
]
},
{
"id":5
},
{
"id":4
}
]
Code Behind work :
public class GetId
{
public string id { get; set; }
}
List<GetId> myDeserializedObjList = (List<GetId>)Newtonsoft.Json.JsonConvert.DeserializeObject(nestable_list_1_output.InnerText, typeof(List<GetId>));
for(int i=0; i < myDeserializedObjList.Count;i++)
{
string Id = myDeserializedObjList[i].id.ToString();
}
after implement this i get only 1,5,4 ...also i want children object Ids..
Thanks
Json to c# converters will not work great with this recursive definitions. In fact your class is simple
public class ID
{
public int id { get; set; }
public List<ID> children { get; set; }
}
Your json can now be in any depth. All you have to do is
var myDeserializedObjList = JsonConvert.DeserializeObject<List<ID>>(json);
You can now get the results recursively
PrintMyObject(myDeserializedObjList);
void PrintMyObject(List<ID> ids)
{
foreach (var id in ids)
{
Console.WriteLine(id.id);
if (id.children != null)
PrintMyObject(id.children);
}
}
To make the answer complete: This part is a Linq trick, to flatten your recursive data structure.
Func<List<ID>, IEnumerable<ID>> flattenList = null;
flattenList = l => l.SelectMany(x => new[] { x }.Concat( x.children==null
? Enumerable.Empty<ID>()
: flattenList(x.children)));
var flatList = flattenList(myDeserializedObjList).ToList();
Use json2csharp to generate correct class structure -
public class Child2
{
public int id { get; set; }
}
public class Child
{
public int id { get; set; }
public List<Child2> children { get; set; }
}
public class RootObject
{
public int id { get; set; }
public List<Child> children { get; set; }
}
Use this class RootObject to deserialize json response.
You should include children in your definition of the GetId class.
public class GetId
{
public int id { get; set; }
public List<GetId> children { get; set; }
}
Then when collecting the id's, you do it using a recursive call to make sure you get all ids in the lower structures.
private void CollectIdValues(List<GetId> list, List<int> ids){
foreach(GetId item in list){
ids.Add(item.id);
if(item.children != null){
CollectIdValues(item.children, ids);
}
}
}
List<GetId> myDeserializedObjList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<GetId>>(jsonString));
List<int> allIds = new List<int>();
CollectIdValues(myDeserializedObjList, allIds);
This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Closed 8 years ago.
I am trying to deserialize a Json response from an API.
The data looks like this
{
"response": {
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
C# class:
public class Products
{
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public Product product { get; set; }
}
public class ss
{
public Dictionary<string, Products.Product> Response { get; set; }
public string total_records { get; set; }
}
Serialization code
ss res = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(jsonData());
I can get it to work without the total_records entry and below by deserializng to a Dictionary <string , Product>. But I cannot figure out how to get it to work. This is the error I get
Error converting value "20" to type 'Products+Product'. Path 'response.total_records'
I know why I get the error, but I'm unsure how I can proceed without going in and substringing from total_records down. I have no control over the API data.
Edit: you guys are fast, I was still getting to putting the classes up
First you json is not valid one, it should look like this
{
"response":{
"6112":{
"ID":"6112",
"Title":"Additional Photos",
},
"5982":{
"ID":"5982",
"Title":"Bike Ride",
},
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
If you mean the response to contain list it should look like this
{
"response":{
"myArray": [
{
"ID":"6112",
"Title":"Additional Photos",
},
{
"ID":"5982",
"Title":"Bike Ride",
}
],
"total_records": "20",
"returned_count": "10",
"returned_records": "1-10",
}
}
So your code look like this
public class MyArray
{
public string ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public List<MyArray> myArray { get; set; }
public string total_records { get; set; }
public string returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
If you have control over API response then please refer to Mzf's answer.
If you don't have control over API then it may not be possible to do this particular deserialization on one go. You might have to loop.
Here's my take.
Update
Modified my approach:
Created a class Response which inherits from Dictionary<string, Product>, and added the metadata parts like total_records, records_count to it's public properties. And created a JsonConverter that can deserialize JObject to Response class.
The logic used for deserialization is quite simple:
Extract the metadata parts like total_records, records_count to variables.
Then remove those metadata from the JObject, so that the key values becomes homogeneous.
Now Json.net will be easily able to serialize JObject to Response object, as key values are homogenous.
Assign the metadata extracted previously to the properties of Response object
public void Deserialize()
{
var json = #"{
'response':{
'6112':{
'ID':6112,
'Title':'Additional Photos',
},
'5982':{
'ID':5982,
'Title':'Bike Ride',
},
'total_records': '20',
'returned_count': 10,
'returned_records': '1-10',
}
}";
var responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<ss>(json, new ResponseConverter());
}
public class Response : Dictionary<string, Product>
{
public int total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Title { get; set; }
}
public class ss
{
public Response Response { get; set; }
}
public class ResponseConverter : Newtonsoft.Json.JsonConverter
{
private Response CreateResponse(Newtonsoft.Json.Linq.JObject jObject)
{
//preserve metadata values into variables
int total_records = jObject["total_records"].ToObject<int>();
var returned_records = jObject["returned_records"].ToObject<string>();
var returned_count = jObject["returned_count"].ToObject<int>();
//remove the unwanted keys
jObject.Remove("total_records");
jObject.Remove("returned_records");
jObject.Remove("returned_count");
//once, the metadata keys are removed, json.net will be able to deserialize without problem
var response = jObject.ToObject<Response>();
//Assign back the metadata to response object
response.total_records = total_records;
response.returned_count = returned_count;
response.returned_records = returned_records;
//.. now person can be accessed like response['6112'], and
// metadata can be accessed like response.total_records
return response;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Response);
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
Response target = CreateResponse(jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
In my opinion this is how the JSON file should look like:
{
"response": {
"5982": {
"ID": 5982,
"Title": "BikeRide"
},
"6112": {
"ID": 6112,
"Title": "AdditionalPhotos"
},
"total_records": "20",
"returned_count": 10,
"returned_records": "1-10"
}
}
and this is how the class should look like
public class __invalid_type__5982
{
public int ID { get; set; }
public string Title { get; set; }
}
public class __invalid_type__6112
{
public int ID { get; set; }
public string Title { get; set; }
}
public class Response
{
public __invalid_type__5982 __invalid_name__5982 { get; set; }
public __invalid_type__6112 __invalid_name__6112 { get; set; }
public string total_records { get; set; }
public int returned_count { get; set; }
public string returned_records { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}