I have following JSON and I am using Json.NET (Newtonsoft.Json):
{
"total_items": "62",
"page_number": "6",
"page_size": "10",
"page_count": "7",
"cars": {
"car": [
{
"car_name": "Honda",
"engines": {
"engine": [ <-- HONDA has multiple engines, so this is an array
{
"name": "1.2L"
},
{
"name": "1.8L"
}
]
},
"country": "Japan"
"image": {
"thumb": {
"url": "http://image_path/Honda.jpg" <-- Image provided
}
}
},
{
"car_name": "Ford",
"engines": {
"engine": { <-- FORD has single engine, so this is an object
"name": "2.2L"
}
},
"country": "Japan"
"image": null <-- image is null
},
{
"car_name": "VW",
"engines": null, <-- VW has no engines, so this is null
"country": "Germany"
"image": null <-- image is null
}
]
}
}
And I have following Car object:
class Car
{
public Car() { }
public string Name { get; set; }
public string Country { get; set; }
public List<String> EngineNames { get; set; }
}
I need to handle all 3 cases above (array for HONDA, object for FORD, null for VW). If it is not null, then get all engine names. So, for example above, my EngineNames list for the 3 cars would be:
Honda.EngineNames = {"1.2L", "1.8L"} // array in JSON
Ford.EngineNames = {"2.2L"} //object in JSON
VW.EngineNames = null //null in JSON
I need to parse the JSON above to get car data. I am parsing car_name and country but I don't know how to parse all engine names by handling the 3 situations above.
private Cars GetCars(string json)
{
dynamic data = (JObject)JsonConvert.DeserializeObject(json);
foreach (dynamic d in data.cars.car)
{
Car c = new Car();
c.Name = (string)d.SelectToken("car_name");
c.Country = (string)d.SelectToken("country");
// PROBLEM: This works fine for array or null in JSON above (HONDA and VW), but it errors on JSON object (in case of FORD)
// When handling FORD, I get error "'Newtonsoft.Json.Linq.JProperty' does not contain a definition for 'name'"
c.EngineNames = (d.engines != null ? ((IEnumerable)d.engines.engine).Cast<dynamic>().Select(e => (string)e.name) : null);
CarList.Add(c);
}
return CarList;
}
Using the converter from here (originally a proposed duplicate, but this question had some other issues with the JSON)
Your class structure needs to be modified a bit.
Looking at this JSON:
"cars": { <-- cars is an object, not an array
"car": [ <-- the cars object actually contains the array
{
"car_name": "Honda",
"engines": { <-- same goes for this
"engine": [
{
Therefore, you'll need to write wrapper classes to properly reflect the JSON. Here's what I've come up with:
public class Root
{
public CarHolder Cars {get;set;}
}
public class CarHolder
{
public IList<Car> Car { get; set; }
}
public class Car
{
public Car() { }
public string car_name { get; set; }
public string Country { get; set; }
public EngineHolder Engines { get; set; }
}
public class EngineHolder
{
[JsonConverter(typeof(SingleOrArrayConverter<Engine>))]
public List<Engine> Engine { get; set; }
}
public class Engine
{
public string Name { get; set; }
}
And using the convert from the above question:
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Usage:
var result = JsonConvert.DeserializeObject<Root>(jsonStr);
Console.WriteLine(result.Cars.Car[0].Engines.Engine[0].Name == "1.2L");
Console.WriteLine(result.Cars.Car[0].Engines.Engine[1].Name == "1.8L");
Console.WriteLine(result.Cars.Car[1].Engines.Engine[0].Name == "2.2L");
Console.WriteLine(result.Cars.Car[2].Engines == null);
All print true
Looping through the cars & engines
foreach(var car in result.Cars.Car)
{
if (car.Engines != null)
{
foreach(var engine in car.Engines.Engine)
{
var engineName = engine.Name;
}
}
}
You should be able to use this as your class structure;
public class Rootobject
{
public string total_items { get; set; }
public string page_number { get; set; }
public string page_size { get; set; }
public string page_count { get; set; }
public Cars cars { get; set; }
}
public class Cars
{
public Car[] car { get; set; }
}
public class Car
{
public string car_name { get; set; }
public Engines engines { get; set; }
public string country { get; set; }
}
public class Engines
{
public object engine { get; set; }
}
//I created below class manually
public class Engine
{
public string name { get; set; }
}
I used inbuilt functionality of VS to generate this. Steps;
Open a new cs file.
Copy your json
Go to Edit menu> Paste special
Select Paste JSON as classes
Once this is done, it should be just a matter of creating two methods to serialize and deserialize.
Updated with serialise/deserialise methods
private static T Deserialise<T>(string json)
{
var myopject = JsonConvert.DeserializeObject<T>(json);
return myopject;
}
private static string Serialise<T>(T value)
{
var mycontent = JsonConvert.SerializeObject(value);
return mycontent;
}
Now to test above methods, you can do this.
var jsonstring = #"{
""total_items"": ""62"",
""page_number"": ""6"",
""page_size"": ""10"",
""page_count"": ""7"",
""cars"": {
""car"": [
{
""car_name"": ""Honda"",
""engines"": {
""engine"": [
{
""name"": ""1.2L""
},
{
""name"": ""1.8L""
}
]
},
""country"": ""Japan""
},
{
""car_name"": ""Ford"",
""engines"": {
""engine"": {
""name"": ""2.2L""
}
},
""country"": ""Japan""
},
{
""car_name"": ""VW"",
""engines"": null,
""country"": ""Germany""
}
]
}
}";
var myobject = Deserialise<Rootobject>(jsonstring);
//if you want to parse engines you can do something like this.
if (myobject.cars != null && myobject.cars.car != null && myobject.cars.car.Any())
{
foreach (Car car in myobject.cars.car)
{
if (car.engines != null && car.engines.engine != null)
{
bool isList = false;
try
{
var eng = Deserialise<Engine>(car.engines.engine.ToString());
}
catch
{
isList = true;
}
if (isList)
{
try
{
var eng = Deserialise<List<Engine>>(car.engines.engine.ToString());
}
catch
{
Debug.WriteLine("Not a list");
}
}
}
}
}
var myjson = Serialise(myobject);
Related
I have below json which I need to deserialize in C# -
{
"clientSettings":[
{
"clientId":12345,
"entries":[
{
"key":"abc",
"value":false
},
{
"key":"def",
"value":false
},
{
"key":"ghi",
"value":false
}
]
},
{
"clientId":9876,
"entries":[
{
"key":"lkmn",
"value":false
}
]
}
],
"productSettings":[
{
"productId":11,
"entries":[
{
"key":"jkl",
"value":true
},
{
"key":"mno",
"value":true
}
]
},
{
"productId":12,
"entries":[
{
"key":"jkl",
"value":true
},
{
"key":"mno",
"value":true
}
]
}
],
"customerSettings":[
{
"key":"enableData",
"value":false
},
{
"key":"minPriceValue",
"value":"10.28"
},
{
"key":"presentData",
"value":"AEGIS"
}
],
"thothTest":{
"9876":[
"K"
],
"5431":[
"A",
"L"
],
"5123":[
"L"
]
},
"osirisTest":{
"7678":[
"K"
]
}
}
Below is the classes I created to deserialzie json into -
public class ProcessHolder : Holder
{
public IDictionary<int, ISet<string>> OsirisTest { get; set; }
public IDictionary<int, ISet<string>> ThothTest { get; set; }
}
public class Holder
{
public IList<Mapping> CustomerSettings { get; set; }
public IList<ClientSettingsMapping> ClientSettings { get; set; }
public IList<ProductSettingsMapping> ProductSettings { get; set; }
}
public class Mapping
{
public string Key { get; set; }
public object Value { get; set; }
}
public class ProductSettingsMapping : Mapping
{
public int ProductId { get; set; }
}
public class ClientSettingsMapping : Mapping
{
public int ClientId { get; set; }
}
I want to load all customerSettings values into CustomerSettings object of Holder class.
Similarly I want to load all clientSettings values into ClientSettings object of Holder class.
Similarly all productSettings values into ProductSettings object of Holder class.
Similarly thothTest values into ThothTest and osirisTest values into OsirisTest object.
I was trying with below code but somehow I am not able to see Key and Value variables being populated inside Mapping class object after deserializing my above json.
private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
public static void Main(string[] args)
{
var jsonContent = File.ReadAllText("/beta/Downloads/test.json");
var config = JsonConvert.DeserializeObject<ProcessHolder>(jsonContent, serializerSettings);
if (config == null)
{
Console.WriteLine("Some Parsing Issue");
}
// using config object here
}
What is wrong I am doing here? I just need to deserialize my json into above classes and give me ProcessHolder object back which I can use later on.
Looking at your JSON, clientSettings and productSettings have entries array. There is no corresponding matching property in your class definition for same.
I would suggest to modify your class definition as per JSON. Try below and see if it helps:
public class ProductSettingsMapping
{
public int ProductId { get; set; }
public IList<Mapping> Entries { get; set; }
}
public class ClientSettingsMapping
{
public int ClientId { get; set; }
public IList<Mapping> Entries { get; set; }
}
Second option - if you are not able to modify classes, you can amend JSON. I am providing sample for clientSettings
{
"clientSettings": [
{
"clientId": 12345,
"key": "abc",
"value": false
},
{
"clientId": 12345,
"key": "def",
"value": false
},
{
"clientId": 12345,
"key": "ghi",
"value": false
},
{
"clientId": 9876,
"key": "lkmn",
"value": false
}
]
}
You can update productSettings accordingly.
Third option is to parse into JObject and then convert to your classes.
A sample for clientSettings
static void Main(string[] args)
{
var json = "{'clientSettings':[{'clientId':12345,'entries':[{'key':'abc','value':false},{'key':'def','value':false},{'key':'ghi','value':false}]},{'clientId':9876,'entries':[{'key':'lkmn','value':false}]}],'productSettings':[{'productId':11,'entries':[{'key':'jkl','value':true},{'key':'mno','value':true}]},{'productId':12,'entries':[{'key':'jkl','value':true},{'key':'mno','value':true}]}],'customerSettings':[{'key':'enableData','value':false},{'key':'minPriceValue','value':'10.28'},{'key':'presentData','value':'AEGIS'}],'thothTest':{'9876':['K'],'5431':['A','L'],'5123':['L']},'osirisTest':{'7678':['K']}}";
var parsed = JObject.Parse(json);
var parsedClientSettings = parsed["clientSettings"];
List<ClientSettingsMapping> clientSettings = new List<ClientSettingsMapping>();
foreach (var parsedClientSetting in parsedClientSettings)
{
var clientId = parsedClientSetting.Value<int>("clientId");
foreach (var entry in parsedClientSetting["entries"])
{
clientSettings.Add(new ClientSettingsMapping { ClientId = clientId, Key = entry["key"].ToString(), Value = entry["value"].ToString() });
}
}
ProcessHolder processHolder = new ProcessHolder() { ClientSettings = clientSettings };
Console.ReadLine();
}
You should define "entries" in both ProductSettingsMapping and ClientSettingsMapping class.
public class ProductSettingsMapping
{
public int ProductId { get; set; }
public IList<Mapping> entries { get; set; }
}
public class ClientSettingsMapping
{
public int ClientId { get; set; }
public IList<Mapping> entries { get; set; }
}
I'm trying to deserialize some partial JSON received from an old SignalR service.
This is an example of the original JSON:
{
"opt": {
"data": {
"DR": [{
"O": [
null,
"18:46.401",
"RGGW.GWWWR",
"4.1",
19,
"17.852",
"42.455",
"",
null,
"+3.893",
"277",
"306",
"",
"310",
"+0.058",
null
],
"OC": [
"1"
]
},
{
"O": [
null,
"1:41.119",
"GYYG.WWWWW",
"1.0",
2,
"17.561",
"43.485",
"40.073",
null,
"+16.772",
"275",
"291",
"218",
"291",
"+16.772",
null
],
"OC": [
"1"
]
}
]
}
}
}
and this the partial JSON received:
{
"opt": {
"data": {
"DR": {
"1": {
"O": {
"2": "WYYW.WWWWW",
"7": "42.283",
"12": "212"
}
}
}
}
}
}
and these are my classes:
public class DR2
{
[JsonProperty("O")]
public List<object> O { get; set; }
[JsonProperty("OC")]
public List<string> OC { get; set; }
}
public class Data4
{
[JsonProperty("DR")]
public List<DR2> DR { get; set; }
}
public class Opt
{
[JsonProperty("data")]
public Data4 data { get; set; }
}
public class SPFeed
{
[JsonProperty("opt")]
public Opt opt { get; set; }
}
Trying to deserialize I receive the classic error:
JsonSerializationException: Cannot deserialize the current JSON object ... because the type requires a JSON array.
SPFeed partial_opt = JsonConvert.DeserializeObject<SPFeed>(test); // The error above
Also, trying to merge the 2 JSON with the code below, no merge made, but only substitution:
var object1 = JObject.Parse(JsonConvert.SerializeObject(spfeed));
var object2 = JObject.Parse(JsonConvert.SerializeObject(partial_opt));
object1.Merge(object2, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Merge
});
I think because the partial JSON in not a correct array.
Any other method to deserialize?
Resolved:
var obj = JObject.Parse(partialJson);
Opt opt = root.SPFeed.opt;
if (obj["opt"]["data"]["DR"] != null) {
IList<JToken> DR = JObject.Parse(obj["opt"]["data"]["DR"].ToString());
var DRindex = Convert.ToInt32(((JProperty)DR[0]).Name);
var O = ((JProperty)DR[0]).Value;
JToken Otemp = JToken.Parse(O.ToString());
if (Otemp["O"] != null)
{
var Oindex = Otemp["O"];
foreach (JToken item in Oindex)
{
int index = Convert.ToInt32(((JProperty)item).Name);
string value = ((JProperty)item).Value.ToString();
Console.WriteLine("Index {0} Value {1}", index, value);
opt.data.DR[DRindex].O[index] = value;
}
}
};
This may not be the cleanest solution, but if you can detect if the JSON is partial or not, then you could use a Custom Contract Resolver. This would also required some changes to your class structure. Redefining structure as
public interface IData{}
public class Data4Partial:IData
{
[JsonProperty("DR")]
public Dictionary<string,Dictionary<string,Dictionary<string,string>>> Data{get;set;}
}
public class DR2Full:IData
{
[JsonProperty("O")]
public List<object> O { get; set; }
[JsonProperty("OC")]
public List<string> OC { get; set; }
}
public class Data4Full:IData
{
[JsonProperty("DR")]
public List<DR2Full> DR { get; set; }
}
public class Opt
{
[JsonProperty("data")]
public IData data { get; set; }
}
public class SPFeed
{
[JsonProperty("opt")]
public Opt opt { get; set; }
}
The Contract Resolver would aim to replace the PropertyType so that result could be adapted to Partial and Full Json. For example,
public class GenericContractResolver<T> : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.UnderlyingName == "data")
{
property.PropertyType = typeof(T);
}
return property;
}
}
You could now use it as
var partialResult = JsonConvert.DeserializeObject<SPFeed>(partialJson, new JsonSerializerSettings
{
ContractResolver = new GenericContractResolver<Data4Partial>()
});
Or
var fullResult = JsonConvert.DeserializeObject<SPFeed>(fullJson
, new JsonSerializerSettings
{
ContractResolver = new GenericContractResolver<Data4Full>()
});
Partial Json Output
Complete Json Output
I need to convert firebase-ish data structure to C# object using JSON.Net library.
I store my data on Firebase Database like following;
{
"PoolDatas": {
"-LGGJGTAv_DPtzkmjIbl": {
"CoinType": 2,
"PoolType": 4,
"Alias": "First Alias",
"Address": "0xAE12EF212",
"Alarms": {
"-LGsdsdv_DPtzkmjIbl": {
"Threshold": {
"Type": 2,
"Val": 100
},
"AlarmType": 3
},
"-LBAsdsdv_DPtzkmjIbl": {
"Threshold": {
"Type": 1,
"Val": 1
},
"AlarmType": 2
}
}
},
"-LEAJGTAv_DPtzkmjIbl": {
"CoinType": 1,
"PoolType": 1,
"Alias": "Second Alias",
"Address": "0xAeeeEF212",
"Alarms": {
"-LGsdsdv_DPtzkmjIbl": {
"Threshold": {
"Type": 10,
"Val": 120
},
"AlarmType": 1
},
"-LBAsdsdv_DPtzkmjIbl": {
"Threshold": {
"Type": 1,
"Val": 250
},
"AlarmType": 2
}
}
}
}
}
All list objects have firebase generated ID. I cannot map this data to C# class because firebase IDs don't comply with the list structure.
Please see my model in C# side;
public class PoolData
{
public string FirebaseId{ get; set; }
public string Alias { get; set; }
public PoolType PoolType { get; set; } //enum
public CoinType CoinType { get; set; } //enum
public string Address { get; set; }
public List<Alarm> Alarms { get; set; }
}
public class Alarm
{
public string FirebaseId{ get; set; }
public AlarmType AlarmType{ get; set; } //enum
public Threshold Threshold { get; set; } //object
}
public class Threshold
{
public ThresholdType Type{ get; set; } //enum
public int Value { get; set; }
}
In order to be able to convert Firebase-ish json into C# object I need a json like this;
{
"PoolDatas": [
{
"FirebaseId": "-LGGJGTAv_DPtzkmjIbl",
"CoinType": 1,
"PoolType": 1,
"Alias": "First Alias",
"Alarms": [
{
"FirebaseId": "-LGsdsdv_DPtzkmjIbl",
"Threshold": {
"Type": 1,
"Val": 1
},
"AlarmType": 1
},
{
"FirebaseId": "-LBAsdsdv_DPtzkmjIbl",
"Threshold": {
"Type": 1,
"Val": 1
},
"AlarmType": 2
}
],
"Address": "0xAE12EF212"
},
{
"FirebaseId": "-LEAJGTAv_DPtzkmjIbl",
"CoinType": 1,
"PoolType": 1,
"Alias": "First Alias",
"Alarms": [
{
"FirebaseId": "-LGsdsdv_DPtzkmjIbl",
"Threshold": {
"Type": 1,
"Val": 1
},
"AlarmType": 1
},
{
"FirebaseId": "-LBAsdsdv_DPtzkmjIbl",
"Threshold": {
"Type": 1,
"Val": 1
},
"AlarmType": 2
}
],
"Address": "0xAE12EF212"
}
]
}
How can I obtain this json from the first one?
I tried several recursive processes using JObject loops but it didn't work.
Thanks in advance!
You can deserialize the JSON to a Dictionary<string, PoolData>, change each value so the key of the dictionary goes into the FirebaseId property, and then put it into an array, something like this:
class Origin
{
public Dictionary<string, PoolData> PoolDatas { get; set; }
}
class Destination
{
public PoolData[] PoolDatas { get; set; }
}
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText("data.json");
var data = JsonConvert.DeserializeObject<Origin>(json);
var destination = new Destination();
destination.PoolDatas = data.PoolDatas.Select(i =>
{
i.Value.FirebaseId = i.Key;
return i.Value;
}).ToArray();
}
}
You will need to install and import the Json.Net package using Newtonsoft.Json;.
EDIT: as mentioned in the comments, this will not work for nested objects. For that scenario, maybe you could use a custom serializer, something like this:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApp1
{
public interface IFirebaseObject
{
string FirebaseId { get; set; }
}
public class PoolData
{
public string FirebaseId { get; set; }
public string Alias { get; set; }
public string Address { get; set; }
[JsonConverter(typeof(MapToArrayConverter<Alarm>))]
public List<Alarm> Alarms { get; set; }
}
public class Alarm: IFirebaseObject
{
public string FirebaseId { get; set; }
public Threshold Threshold { get; set; } //object
}
public class Threshold
{
public int Value { get; set; }
}
class Origin
{
public Dictionary<string, PoolData> PoolDatas { get; set; }
}
class Destination
{
public PoolData[] PoolDatas { get; set; }
}
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText("data.json");
var data = JsonConvert.DeserializeObject<Origin>(json);
var destination = new Destination();
destination.PoolDatas = data.PoolDatas.Select(i =>
{
i.Value.FirebaseId = i.Key;
return i.Value;
}).ToArray();
}
}
public class MapToArrayConverter<T> : JsonConverter where T : IFirebaseObject
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject item = JObject.Load(reader);
Dictionary<string, T> value = JsonConvert.DeserializeObject<Dictionary<string, T>>(item.ToString());
// TODO also consider single values instead of lists
return value.Select(i =>
{
i.Value.FirebaseId = i.Key;
return i.Value;
}).ToList();
} else
{
return null;
}
}
public override bool CanConvert(Type objectType)
{
// TODO validate the object type
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// TODO implement the reverse method to also write
throw new NotImplementedException();
}
}
}
But as you can see the solution starts to get somewhat complex. Unless you need to do this for many types, maybe manually converting would be less time consuming and simpler to maintain.
How to parse this JSON
using Newton Soft
I tried but gives me null as my modal should have class 1, 2, 3 ...
but that's dynamic .So getting confuse.
Appreciate your help !
{
"data": {
"1": {
"test": {
"col1": "123",
"col2": "name"
}
},
"2": {
"test": {
"col1": "345",
"col2": "name2"
}
},
"3": {
"test": {
"col1": "456",
"col2": "name3"
}
}
}
class root
{
data data{get; set;};
}
class data
{
List<object> innerObject {get; set;} //not sure as labels are dynamic
}
class test
{
col1{get; set;}
col2{get; set;}
} //Calling it in that way ..
root r = JsonConvert.DeserializeObject<root>(result);
You can use a dictionary to parse a JSON object with custom property names:
public class Test
{
public string col1 { get; set; }
public string col2 { get; set; }
}
public class DataValue
{
public Test test { get; set; }
}
public class RootObject
{
public RootObject() { this.data = new Dictionary<string, DataValue>(); }
public Dictionary<string, DataValue> data { get; set; }
}
See Deserialize a Dictionary.
If you are sure the dictionary keys will always be numeric, you can use integer keys, and use a SortedDictionay to order the values:
public class RootObject
{
public RootObject() { this.data = new SortedDictionary<long, DataValue>(); }
public SortedDictionary<long, DataValue> data { get; set; }
}
#dbc's answer is a good option, but you have a few other options as well:
If you have control over the JSON that's being generated, make your life easier by using an Array if you can. It looks like what you would really want is this:
{
"data": [{
"test": {
"col1": "123",
"col2": "name"
}, {
"test": {
"col1": "345",
"col2": "name2"
}, /* etc */
]
}
This way, data represents an array and you can deserialize it as such:
class Root
{
List<Data> Data { get; set; };
}
class Data
{
Test Test { get; set; }
}
JsonConvert.DeserializeObject<Root>(json);
You could force your JSON into an array structure using a custom converter. This is making a lot of assumptions about your JSON. For example, it's assuming that you have an object with a integer keys, with no spaces in between numbers:
public class ObjectAsArrayConverter : JsonConverter
{
public override bool CanConvert(Type type)
{
return
type.IsGenericType &&
typeof(List<>) == type.GetGenericTypeDefinition() &&
typeof(Data) == type.GetGenericArguments()[0];
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JObject obj = JObject.ReadFrom(reader).ToObject<JObject>();
return obj.Properties()
.Select(p => new { Index = int.Parse(p.Name), Value = obj[p.Name].ToObject<Data>() })
.OrderBy(p => p.Index)
.Select(p => p.Value)
.ToList();
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
This would allow you to deserialize your JSON into this structure:
class Root
{
public List<Data> Data {get; set;}
}
class Data
{
public Test Test { get; set; }
}
class Test
{
public string Col1 {get; set;}
public string Col2 {get; set;}
}
Example: https://dotnetfiddle.net/e2Df7h
You might need to tweak the second suggestion, for example if you expect the array to be sparse you might want to make the code expect that.
I have following JSon and I am using Json.NET (Newtonsoft.Json):
{
"total_items": "62",
"page_number": "6",
"page_size": "10",
"page_count": "7",
"cars": {
"car": [
{
"car_name": "Honda",
"engines": {
"engine": [
{
"name": "1.2L"
},
{
"name": "1.8L"
}
]
},
"country": "Japan"
},
{
"car_name": "Ford",
"engines": {
"engine": {
"name": "2.2L"
}
},
"country": "Japan"
},
{
"car_name": "VW",
"engines": null,
"country": "Germany"
}
]
}
}
And I have following Car object:
class Car
{
public Car() { }
public string Name { get; set; }
public string Country { get; set; }
public List<String> EngineNames { get; set; }
}
I need to handle case if "engines=null". If it is not null, then get all engine names. So, for example above, my EngineNames list for Honda and VW would be:
Honda.EngineNames = {"1.2L", "1.8L"} // has 2 names
VW.EngineNames = null //has nothing since no engine is provided
I need to pars the JSON above to get car data. I am parsing car_name and country but I dont know how to parse all engine names in array of engines (array could be null).
private Cars GetCars(string json)
{
dynamic data = (JObject)JsonConvert.DeserializeObject(json);
foreach (dynamic d in data.cars.car)
{
Car c = new Car();
c.Name = (string)d.SelectToken("car_name");
c.Country = (string)d.SelectToken("country");
c.EngineNames = //HOW TO GET ALL ENGINE NAMES AND HANDLE NULL ?
CarList.Add(c);
}
return CarList;
}
The best approach is to cut this dynamic a-la-javascript kind of crap out and just define your strongly typed models that will match your JSON structure:
public class Wrapper
{
public Cars Cars { get; set; }
}
public class Cars
{
public Car[] Car { get; set; }
}
public class Car
{
[JsonProperty(PropertyName = "car_name")]
public string Name { get; set; }
public string Country { get; set; }
public Engines Engines { get; set; }
}
public class Engines
{
public Engines()
{
Engine = new Engine[0];
}
// We need to use a custom JSON converter
// because of this pretty broken schema that you have
// in which the engine property can be array and a standard
// object at the same time
[JsonConverter(typeof(EnginesConverter))]
public Engine[] Engine { get; set; }
}
public class Engine
{
public string Name { get; set; }
}
and then just let JSON.NET do the magic of converting this string back into your strongly typed objects:
var wrapper = JsonConvert.DeserializeObject<Wrapper>(json);
and now that you have a strongly typed structure, you could more than easily map this to a desired C# DTO using LINQ:
public class CarsDto
{
public CarsDto()
{
Engines = new List<string>();
}
public string Name { get; set; }
public string Country { get; set; }
public List<string> Engines { get; set; }
}
and then:
var dto = wrapper.Cars.Car.Select(c => new CarsDto
{
Name = c.Name,
Country = c.Country,
Engines = (c.Engines ?? new Engines()).Engine.Select(e => e.Name).ToList(),
}).ToList();
and finally here's the custom JSON converter that we used:
public class EnginesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<Engine[]>(reader);
}
else
{
Engine e = serializer.Deserialize<Engine>(reader);
return new[] { e };
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Why not continue using dynamic typing, and access object properties as dynamics, so you can do:
var car = new Car();
car.Name = (string)d.car_name;
car.EngineNames = (d.engines != null ? ((IEnumerable)d.engines).Cast<dynamic>().Select(e => (string)e.name) : null);