Deserialize JSON single string to array - c#

I am trying to deserialize my json data however my class is not set up properly I do not have access to change the json response, so I need to write a function to handle the json properly.
Here is the data
{
"blabla": {
"-Score": "1",
"-Ref": "50",
"foo": {
"-colour": "Yellow",
"-ref": "y50"
}
}
}
however some times the data will be
{
"blabla": {
"-Score": "1",
"-Ref": "50",
"foo": [
{
"-colour": "Yellow",
"-ref": "y50"
},
{
"-colour": "Green",
"-ref": "g50"
},
{
"-colour": "Red",
"-ref": "r50"
}
]
}
}
This class works for the first data
public class blabla
{
public Foo Foo {get; set;}
}
And this class works for the second data
public class blabla
{
public Foo[] Foo {get; set;}
}
But how can I get the class to work for both?

Here are the basic classes:
public class Test
{
public Blabla blabla { get; set; }
}
public class Blabla
{
public string _score { get; set; }
public string _ref { get; set; }
[JsonConverter(typeof(FooConverter))]
public Foo[] foo { get; set; }
}
public class Foo
{
public string _colour { get; set; }
public string _ref { get; set; }
}
Set type of foo to be Foo[] no matter what the data is, and add [JsonConverter(typeof(FooConverter))] to use a custom converter.
Here is the custom converter:
public class FooConverter : JsonConverter
{
// Declared as abstract in JsonConverter so must be overridden
public override bool CanConvert(Type objectType) { return true; }
// Declared as abstract in JsonConverter so must be overridden
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
return token.Type == JToken.Array ? token.ToObject<Foo[]>() : new Foo[] { token.ToObject<Foo>() };
}
}
In the ReadJson method we load the data in a token and we check whether the data is an array or a plain object. If it is already an array we just return the array object as Foo[] and if it is a plain object we return a new Foo[] containing our plain object.
Here is a test case:
string json1 = #"{
""blabla"":
{
""_score"": ""1"",
""_ref"": ""50"",
""foo"":
{
""_colour"": ""Yellow"",
""_ref"": ""y50""
}
}
}";
string json2 = #"{
""blabla"":
{
""_score"": ""1"",
""_ref"": ""50"",
""foo"":
[
{
""_colour"": ""Yellow"",
""_ref"": ""y50""
},
{
""_colour"": ""Green"",
""_ref"": ""g50""
},
{
""_colour"": ""Red"",
""_ref"": ""r50""
}
]
}
}";
Test test1 = JsonConvert.DeserializeObject<Test>(json1);
Test test2 = JsonConvert.DeserializeObject<Test>(json2);
You will always have an array but there will be 1 element in the first test case, and 3 elements in the second test case.

go to json2csharp.com
paste your json data
generate the class
there you go :)
using Newtonsoft.Json;
using (WebClient wc = new WebClient())
{
var json = wc.DownloadString(url);
var t = JsonConvert.DeserializeObject<whateverClass.you.have.made>(json);
}

Related

Deserializing a JSON string to a C# class where JSON class may accept either array / object based on a JSON property

I have to deserialize a JSON string to C# classes inorder to bind to a grid. I have implemented the respective classes in C#. But at a particular instance, this fails because the JSON property will be either an array or an object. Please check a part of the string.
I have created ItemList class with 3 properties IL1 , Name and another object class "Item". However, you can see that when the property "Name" is Rubber, I should have List of Item class as a property rather than Item object. When it is Rubber, it returns array of 2 items.
"ItemList": [
{
"#IL1": "Yes",
"#Name": "Pencil"
"Item": {
"#ItemType": "Pencil",
"#Item2": "1A7716744F7048ACA2549BE93F0A2BF1",
"aimLink": {
"#id": "1A7716744F7048ACA2549BE93F0A2BF1",
"#DisplayText": "P00001"
}
}
},
{
"#IL1": "Yes",
"#Name": "Pen",
"Item": {
"#ItemType": "Pen",
"#Item2": "AE067F7EDB6147C09AED243C1F7FAD25",
"aimLink": {
"#id": "AE067F7EDB6147C09AED243C1F7FAD25",
"#DisplayText": "5100010654120001
}
}
},
{
"#IL1": "Yes",
"#Name": "Rubber",
"Item": [
{
"#ItemType": "Rubber",
"#ItemGID": "622025629037499394DF092DA16BAB7F",
"aimLink": {
"#id": "622025629037499394DF092DA16BAB7F",
"#DisplayText": "12345678-1234-123456-7116#01"
}
},
{
"#ItemType": "Rubber",
"#ItemGID": "F336F65F8E014E80B84A2312F829493C"
"aimLink": {
"#id": "F336F65F8E014E80B84A2312F829493C",
"#DisplayText": "12345678-1234-123456-7116#14"
}
}
]
}
],
How can I parse this to a C# class effectively and the easiest way to get this done?
Thanks,
Divya
You can resolve this issue by making your custom JsonConverter and you can use below to convert single value to array then mark Item property with the JsonConverter attribute
public class SingleValueArrayConverter<T> : 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)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override bool CanConvert(Type objectType)
{
return false;
}
}
let's assume this your ItemList class
public class ItemList
{
public string #IL1 { get; set; }
public string #Name { get; set; }
[JsonConverter(typeof(SingleValueArrayConverter<Item>))]
public List<Item> Item { get; set; }
}
public class Item
{
public string #ItemType { get; set; }
public string #Item2 { get; set; }
public AimLink aimLink { get; set; }
}
public class AimLink
{
public string #id { get; set; }
public string #DisplayText { get; set; }
}
You can make your custom JsonConverter (e.g. ItemConverter) and mark Item property with the [JsonConverter(typeof(ItemConverter))] attribute

JSON complex type that can be an object or an array of objects [duplicate]

This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 3 years ago.
I am trying to process an object that can be either an array of object or just the object. Using the code below only works when the naics is an object and not an array. What am I doing wrong?
Here is the shortest example I can come up with:
[{
"section": "52.219-1.b",
"naics": [{
"naicsName": "Engineering Services",
"isPrimary": true,
"ExcpCounter": 1,
"isSmallBusiness": "Y",
"naicsCode": 541330
},
{
"naicsName": "Military and Aerospace Equipment and Military Weapons",
"isPrimary": true,
"ExcpCounter": 2,
"isSmallBusiness": "Y",
"naicsCode": 541330
}
]
},
{
"section": "52.219-1.b",
"naics": {
"naicsName": "Janitorial Services",
"isPrimary": true,
"isSmallBusiness": "Y",
"naicsCode": 561720
}
}
]
I will only have one of the types but I forced two in an array to force it into Quick Type.
My classes are:
[JsonProperty("naics", NullValueHandling = NullValueHandling.Ignore)]
public AnswerNaics Naics { get; set; }
public partial struct AnswerNaics
{
public AnswerNaic Naic;
public AnswerNaic[] NaicArray;
public static implicit operator AnswerNaics(AnswerNaic Naic) => new AnswerNaics { Naic = Naic };
public static implicit operator AnswerNaics(AnswerNaic[] NaicArray) => new AnswerNaics { NaicArray = NaicArray };
}
public partial class AnswerNaic
{
[JsonProperty("naicsName")]
public string NaicsName { get; set; }
[JsonProperty("hasSizeChanged")]
public string HasSizeChanged { get; set; }
[JsonProperty("isPrimary")]
public bool IsPrimary { get; set; }
[JsonProperty("ExcpCounter", NullValueHandling = NullValueHandling.Ignore)]
public long? ExcpCounter { get; set; }
[JsonProperty("isSmallBusiness")]
public string IsSmallBusiness { get; set; }
[JsonProperty("naicsCode")]
public string NaicsCode { get; set; }
}
internal class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AnswerNaics) || t == typeof(AnswerNaics?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<AnswerNaic>(reader);
return new AnswerNaics { Naic = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<AnswerNaic[]>(reader);
return new AnswerNaics { NaicArray = arrayValue };
}
throw new Exception("Cannot unmarshal type AnswerNaics");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (AnswerNaics)untypedValue;
if (value.NaicArray != null)
{
serializer.Serialize(writer, value.NaicArray);
return;
}
if (value.Naic != null)
{
serializer.Serialize(writer, value.Naic);
return;
}
throw new Exception("Cannot marshal type Naics");
}
public static readonly NaicsConverter Singleton = new NaicsConverter();
}
I have more object or array nodes, but I am just trying to figure out one to be able to apply to all of them.
Since you cannot change the incoming JSON, you're going to need a custom converter instead. Something like this for example:
public class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Naics);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var naics = new Naics();
switch (reader.TokenType)
{
case JsonToken.StartObject:
// We know this is an object, so serialise a single Naics
naics.Add(serializer.Deserialize<Naic>(reader));
break;
case JsonToken.StartArray:
// We know this is an object, so serialise multiple Naics
foreach(var naic in serializer.Deserialize<List<Naic>>(reader))
{
naics.Add(naic);
}
break;
}
return naics;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And the supporting classes:
public class Root
{
public string Section { get; set; }
[JsonConverter(typeof(NaicsConverter))]
public Naics Naics { get; set; }
}
// This isn't ideal, but it's quick and dirty and should get you started
public class Naics : List<Naic>
{
}
public class Naic
{
public string NaicsName { get; set; }
public bool IsPrimary { get; set; }
public string IsSmallBusiness { get; set; }
public long NaicsCode { get; set; }
}
And finally, to deserialise:
var settings = new JsonSerializerSettings {Converters = {new NaicsConverter()}};
var root = JsonConvert.DeserializeObject<Root[]>(Json, settings);
Now your object will get serialised into the list, but as a single item.
You can solve this using a dynamic field in your class.
Consider this JSON:
[
{
"field1": "val1",
"nested": [
{
"nestedField": "val2"
},
{
"nestedField": "val3"
}
]
},
{
"field1": "val4",
"nested":
{
"nestedField": "val5"
}
}
]
nested field is first an array with 2 objects and in the second appearance a single object. (similar to the JSON you posted)
So the class representation would look like:
public class RootObject
{
public string field1 { get; set; }
public dynamic nested { get; set; }
public List<NestedObject> NestedObjects
{
get
{
if(nested is JArray)
{
return JsonConvert.DeserializeObject<List<NestedObject>>(nested.ToString());
}
var obj = JsonConvert.DeserializeObject<NestedObject>(nested.ToString());
return new List<NestedObject> { obj };
}
}
}
public class NestedObject
{
public string nestedField { get; set; }
}
The Deserialization code is trivial using Newtonsoft JSON:
var objectList = JsonConvert.DeserializeObject<List<RootObject>>("some_json");
foreach(var v in objectList)
{
foreach(var n in v.NestedObjects)
{
Console.WriteLine(n.nestedField);
}
}
The only change is an implementation of NestedObjects ready only property. It check if the dynamic object is JArray or object. In any case, it returns a List of nested objects.

Deserialize different json property to same model property conditionally

Lets say I have the below json.
{
"allitemscount":2,
"allitems":[
{"itemid":"1","itemname":"one"},
{"itemid":"2","itemname":"two"}],
"customitems":[
{"itemid":"3","itemname":"three"},
{"itemid":"4","itemname":"four"}]
}
and when deserializing this json, it should go to the below C# model.
public class response
{
public int allitemscount;
public List<item> items;
}
public class item
{
public string itemid;
public string itemname;
}
Question:
How to switch between allitems and customitems based on a condition? For eg., if useAllitems is true, allitems from the json to be filled in items and if useCustomItems is true, customitems to be filled in items property. Please help on how to do this.
Using JsonProperty on public List<item> items is allowing to switch between either allitems, but is there a way to deserialize based on the above mentioned condition.
I made it with writing our own ItemConverter which inherited from JsonConverter,
Sample model json that I use in my trying:
{
"allitemscount": 2,
"UseCustomItems": true,
"allitems": [
{
"itemid": "1",
"itemname": "one"
},
{
"itemid": "2",
"itemname": "two"
}
],
"customitems": [
{
"itemid": "3",
"itemname": "three"
},
{
"itemid": "4",
"itemname": "four"
}
]
}
Console application's main method:
static void Main(string[] args)
{
using (StreamReader r = new StreamReader(#"\model.json")) // json path
{
string json = r.ReadToEnd();
var deserializedJson = JsonConvert.DeserializeObject<Result>(json, new ItemConverter());
}
}
Models:
public class Result // main object
{
[JsonProperty("allitemscount")]
public long Allitemscount { get; set; }
public bool UseCustomItems { get; set; }
}
public class ResultA : Result // CustomItems Model
{
[JsonProperty("customitems")]
private List<Item> Items { get; set; }
}
public class ResultB : Result // AllItems Model
{
[JsonProperty("allitems")]
private List<Item> Items { get; set; }
}
public class Item
{
[JsonProperty("itemid")]
public string Itemid { get; set; }
[JsonProperty("itemname")]
public string Itemname { get; set; }
}
And the ItemConverter that we used while deserializing to object:
internal class ItemConverter : JsonConverter
{
private Type currentType;
public override bool CanConvert(Type objectType)
{
return typeof(Item).IsAssignableFrom(objectType) || objectType == typeof(Result);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject item = JObject.Load(reader);
if (item["UseCustomItems"] != null)
{
// save the type for later.
switch (item["UseCustomItems"].Value<bool>())
{
case true:
currentType = typeof(ResultA);
return item.ToObject<ResultA>(); // return result as customitems result
case false:
currentType = typeof(ResultB);
return item.ToObject<ResultB>(); // return result as allitems result
}
return item.ToObject<Result>();
}
// use the last type you read to serialise.
return item.ToObject(currentType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The result should be like bellowing image
I hope this solution help to you
You can deserialize above json by changing your response class as follows,
public class response{
public int allitemscount;
public List<item> allitems;
public List<item> customitems;
}
then use the following code,
var jsonData = "{\"allitemscount\":2, \"allitems\":[{\"itemid\":\"1\",\"itemname\":\"one\"}, {\"itemid\":\"2\",\"itemname\":\"two\"}],\"customitems\":[{\"itemid\":\"3\",\"itemname\":\"three\"},{\"itemid\":\"4\",\"itemname\":\"four\"}]}";
var data = JsonConvert.DeserializeObject<response>(jsonData);
foreach(var str in data.allitems) {
Console.WriteLine(str.itemid +'-'+str.itemname);
}
foreach(var str in data.customitems) {
Console.WriteLine(str.itemid +'-'+str.itemname);
}

Deserializing a JSON Dictionary<int,CustomType> to List<CustomType>

I need to Deserialize a JSON Dictionary of type Dictionary to a List (List), I am using JSON.Net for this purpose. For sure its an amazing Library its just this that I am bit stuck !
I am subscribing to some API the Response is As you can see below:
"team_details": {
"0": {
"index": 1,
"team_id": "1..",
"team_name": "Research Team",
"team_url": "...",
"role": "Administrator"
},
"1": {
"index": 2,
"team_id": "2..",
"team_name": "WB Team",
"team_url": "...",
"role": "User"
}
}
I need to use this to convert it to List<Team> where Teams:
Class Teams{
public int Index{get;set;}
public String TeamName{get;set;}
...
}
The easiest way to do this is to deserialize into a Dictionary<string, Team> and then get the values into a list using dictionary.Values.ToList() when you need them.
However, if you would really rather have a List<Team> in your class definition, you can use a custom JsonConverter to do the conversion during deserialization.
public class TeamListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<Team>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
return token.Values().Select(v => v.ToObject<Team>()).ToList();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Demo:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""team_details"": {
""0"": {
""index"": 1,
""team_id"": ""1.."",
""team_name"": ""Research Team"",
""team_url"": ""..."",
""role"": ""Administrator""
},
""1"": {
""index"": 2,
""team_id"": ""2.."",
""team_name"": ""WB Team"",
""team_url"": ""..."",
""role"": ""User""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (Team team in root.Teams)
{
Console.WriteLine(team.TeamName);
}
}
}
public class RootObject
{
[JsonProperty("team_details")]
[JsonConverter(typeof(TeamListConverter))]
public List<Team> Teams { get; set; }
}
public class Team
{
[JsonProperty("index")]
public int Index { get; set; }
[JsonProperty("team_id")]
public string TeamId { get; set; }
[JsonProperty("team_name")]
public string TeamName { get; set; }
[JsonProperty("team_url")]
public string TeamUrl { get; set; }
[JsonProperty("role")]
public string Role { get; set; }
}
Output:
Research Team
WB Team

Serialize member as Object or List<Object>

This is somewhat similar to JSON.net - field is either string or List
Lets say I have below POCO class and need to deserialze a json string to that type.
public class Movie
{
[JsonProperty("title")]
public List<string> Title { get; set; }
[JsonProperty("images")]
public List<Image> Images { get; set; }
[JsonProperty("actors")]
public List<Actor> Actors { get; set; }
[JsonProperty("directors")]
public List<Director> Directors { get; set; }
}
let's say the json string needed to be deserialized is as follows
{
"title": "Movie title",
"images": [
{"url" : "http://www.url.com/img_3.jpg"},
{"url" : "http://www.url.com/img_4.jpg"}
],
"actors": [
{"name" : "Steven Berkoff"},
{"name" : "Nikolaj Coster-Waldau"},
{"name" : "Julie Cox"}
],
"directors": { "name" : "Simon Aeby" }
},
{
"title": "Movie title",
"images": {"url" : "http://www.url.com/img_3.jpg"},
"actors": {"name" : "Julie Cox"},
"directors": { "name" : "Simon Aeby" }
}
The issue here is even thought the POCO is expecting a list of Directors, Actors and Images, the json only have one of each types. It gives an error when trying to deserialize.
Create a converter for those lists that can recognize if there are one or more items in the list and apply it to your properties:
public class SingleOrEnumerableJsonConverter<TEnumerable> : JsonConverter
where TEnumerable : IEnumerable
{
public override bool CanConvert(Type objectType)
{
return typeof(TEnumerable).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = serializer.Deserialize<JToken>(reader);
return ConvertObject(obj) ?? GetDefaultValue();
}
private object GetDefaultValue()
{
return Activator.CreateInstance<TEnumerable>();
}
private object ConvertObject(JToken obj)
{
try
{
return obj.ToObject<TEnumerable>();
}
catch (JsonSerializationException)
{
// try as an array of object
return new JArray { obj }.ToObject<TEnumerable>();
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
object serializableValue = GetSerializableValue((TEnumerable)value, serializer);
serializer.Serialize(writer, serializableValue);
}
private object GetSerializableValue(TEnumerable items, JsonSerializer serializer)
{
var arr = new JArray(items.Cast<object>()
.Select(item => JToken.FromObject(item, serializer))
);
return arr.Count > 1 ? arr : arr.SingleOrDefault();
}
}
public class Movie
{
[JsonProperty("title")]
[JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<string>>))]
public List<string> Title { get; set; }
[JsonProperty("images")]
[JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Image>>))]
public List<Image> Images { get; set; }
[JsonProperty("actors")]
[JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Actor>>))]
public List<Actor> Actors { get; set; }
[JsonProperty("directors")]
[JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Director>>))]
public List<Director> Directors { get; set; }
}

Categories

Resources