How to deserialize an array of an array of objects using Newtonsoft - c#

I am trying to deserialize an Object like this.
My issue is that it is blowing up trying to deserialize the inner items.
{
"outeritems": [{
"inneritems": [
[{
"itemid": "1"
}, {
"itemid": "2"
}]
]
}]
}
I have already tried
public List<List<inneritems>> inneritems{ get; set; }
also
public List<inneritems> inneritems{ get; set; }
I'm thinking this might have to have a custom JSON converter

Actually vikas is close in anwering your question.
public class Outeritem
{
public List<List<object>> inneritems { get; set; }
}
public class RootValue
{
public List<Outeritem> outeritems { get; set; }
}
[TestMethod]
public void SerializeAndDeserializeTest()
{
var expected = "{\"outeritems\":[{\"inneritems\":[[{\"itemid\":\"1\"},{\"itemid\":\"2\"}]]}]}";
var rootValue = new RootValue
{
outeritems = new List<Outeritem>
{
new Outeritem
{
inneritems = new List<List<object>> {
new List<object> { new {itemid = "1"},new {itemid = "2"} }
}
}
}
};
var actual = JsonConvert.SerializeObject(rootValue);
Assert.AreEqual(expected, actual);
}

Try this
public class Inneritem
{
public String itemid { get; set; }
}
public class Outeritem
{
public List<Inneritem> inneritems { get; set; }
}
public class RootValue
{
public List<Outeritem> outeritems { get; set; }
}

I had to create a custom Newtonsoft JSON Converter
This is what I did.
Created A JsonCreationConverter Base Class
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanWrite is false. The type will skip the converter.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get { return false; }
}
}
Created A Costume Converter that inherited my base class
public class OuterConverter :
JsonCreationConverter<OuterItems>
{
protected override OuterItems Create(Type objectType, JObject jObject)
{
OuterItems outeritems =
new OuterItems();
var properties = jObject.Properties().ToList();
outeritems.InnerItems = GetInnerItems((object)properties[0].Value);
return outeritems;
}
// Need to iterate through list so creating a custom object
private List<List<InnerItems>> GetInnerItems(object propertyValue)
{
string sinneritems = "";
object inneritems = propertyValue;
sinneritems = String.Format("{0}", inneritems);
sinneritems = sinneritems.Insert(1, "{ \"Items\": [");
sinneritems = sinneritems.Substring(1, sinneritems.Length - 1);
sinneritems = sinneritems.Remove(sinneritems.Length - 1);
sinneritems += "]}";
dynamic results = JObject.Parse(sinneritems);
List<List<InnerItems>> innerItemsList = new List<List<InnerItems>>();
List<InnerItems> linnerItems = new List<InnerItems>();
foreach (var items in results.Items)
{
foreach (var item in items)
{
string sItem = String.Format("{0}", item);
InnerItems ninneritems = Newtonsoft.Json.JsonConvert.DeserializeObject<InnerItems>(sItem);
linnerItems.Add(ninneritems);
}
innerItemsList.Add(linnerItems);
}
return innerItemsList;
}
}
Append the New Converter on the Newtonsoft Deserializer
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result.ToString(),
new OuterConverter());

Related

Deserialize JSON dictionary-like to object with properties

I'm stuck with an awful API which returns the following JSON:
{
"root":[
{
"row":[
{
"name":"Foo",
"value":"Some value"
},
{
"name":"Bar",
"value":"Some other value"
}, (...)
]
},
{
"row":[
{
"name":"Foo",
"value":"Lorem"
},
{
"name":"Bar",
"value":"Ipsum"
}, (...)
]
}, (...)
]
}
I'd like to use Newtonsoft.Json to deserialize it as a List of C# objects, so that name will match object property and value will be property value. The class would look like this:
class Row
{
public string Foo { get; set; }
public string Bar { get; set; }
(...)
}
Any ideas?
You can use a custom JsonConverter to handle this. Below a simple example that requires some null checks etc, but this gets the idea:
public class RowConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Row[]);
}
public override bool CanWrite => false;
public override bool CanRead => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
else if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize(reader, objectType);
}
else
{
JObject obj = JObject.Load(reader);
var root = obj["root"];
if (root != null)
{
var rows = new List<Row>();
foreach (var item in root)
{
var row = item["row"];
var newRow = new Row();
foreach(var field in row)
{
// better use reflection here to convert name-value to property setter
if (field.Value<string>("name") == "Foo")
{
newRow.Foo = field["value"].Value<string>();
}
if (field.Value<string>("name") == "Bar")
{
newRow.Bar = field["value"].Value<string>();
}
}
rows.Add(newRow);
}
return rows.ToArray();
}
return null;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then on your Row class:
[JsonConverter(typeof(RowConverter))]
public class Row
{
public string Foo { get; set; }
public string Bar { get; set; }
}
If you need to include it in your MVC, in Startup.cs (asp.net core):
services
.AddMvc()
.AddJsonOptions(options => { options.SerializerSettings.Converters.Add(new RowConverter()); });
As an option you can convert it to dictionary for your single "row" property:
var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
And after create dynamic object:
private static dynamic DictionaryToObject(IDictionary<String, Object> dictionary)
{
var expandoObj = new ExpandoObject();
var expandoObjCollection = (ICollection<KeyValuePair<String, Object>>)expandoObj;
foreach (var keyValuePair in dictionary)
{
expandoObjCollection.Add(keyValuePair);
}
dynamic eoDynamic = expandoObj;
return eoDynamic;
}
private static T DictionaryToObject<T>(IDictionary<String, Object> dictionary) where T : class
{
return DictionaryToObject(dictionary) as T;
}
You can make classes like below then use newtosnsoft
public class root
{
public List<row> row {get;set;}
}
public class row
{
public string name {get;set;}
public string value {get;set;}
}

Simplifying Json entity for deserialisation

Actually I receive Json like this:
{
"id" : 1,
"items": [
{
"media": {
"id": 1,
}
},
{
"media": {
"id": 2,
},
}
],
"status": "ok"
}
I created class like this to map on entity
public class Message
{
[JsonProperty("items")]
public List<Item> Items { get; set; }
}
public class Item
{
[JsonProperty("media")]
public Media Media { get; set; }
}
public class Media
{
[JsonProperty("id")]
public string Id { get; set; }
}
This work perfectly but my question is:
Is it possible to remove Item class and directly cast in List of Media to simplify ?
Update:
I'm pretty sure it is possible, the first way I found before post is JSonConverter, but I'm not sure is the best way to do that, i would like to know if there are any easier solution, with attribute for example.
Using a private Dictionary
Yes, you can, for example using a (private) Dictionary
public class Message
{
public List<Media> Items { get; set; }
}
public class Media
{
[JsonProperty("media")]
private Dictionary<string, string> IdDict;
public string Id
{
get { return IdDict["id"]; }
set { IdDict["id"] = value; }
}
}
Usage
var result = JsonConvert.DeserializeObject<Message>(json);
var test = result.Items[1].Id;
Implementing a Converter
Alternatively you can achieve your result by implementing a Converter
public class MediaConverter : JsonCreationConverter<Message>
{
protected override Message Create(Type objectType, JObject jObject)
{
var msg = new Message();
msg.Items = new List<Media>();
foreach (var item in jObject["items"])
{
Media media = new Media();
media.Id = item["media"]["id"].Value<string>();
msg.Items.Add(media);
}
return msg;
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
return target;
}
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Usage
var result = JsonConvert.DeserializeObject<Message>(json, new MediaConverter());
var test = result.Items[1].Id;

How to deserialize a complex separated ("Column"/"Data") json to c# object using json.net

I need to parse the following JSON into a List of PackageEntity Object.
Since this json is divided into Column and Data, I am having trouble to do so in an intelligent way.
The JSON looks like:
{
"COLUMNS": ["NSHIPMENTID", "NSHIPPINGCOMPANYID", "NUSERID", "NWEIGHT", "NHEIGHT"],
"DATA": [
[7474, null, 12363, "16", "2"],
[7593, null, 12363, "64", "7"]
]
}
I would like to deserialize it to a list of the following class:
public class PackageEntity
{
public int NSHIPMENTID { get; set; }
public string NSHIPPINGCOMPANYID { get; set; }
public int NUSERID { get; set; }
public decimal NWEIGHT { get; set; }
public decimal NHEIGHT { get; set; }
}
what i did so far:
JObject JsonDe = JObject.Parse(responseString);
int length = JsonDe.Property("DATA").Value.ToArray().Count();
List<PackageEntity> _list = new List<PackageEntity>();
for (int i = 0; i < length; i++)
{
PackageEntity pD = new PackageEntity();
pD.NSHIPMENTID = JsonDe.Property("DATA").Value.ToArray()[i][0].ToString();
pD.NSHIPPINGCOMPANYID = JsonDe.Property("DATA").Value.ToArray()[i][1].ToString();
pD.NUSERID = JsonDe.Property("DATA").Value.ToArray()[i][2].ToString();
pD.NWEIGHT = JsonDe.Property("DATA").Value.ToArray()[i][3].ToString();
pD.NHEIGHT = JsonDe.Property("DATA").Value.ToArray()[i][4].ToString();
_list.Add(pD);
}
You can use the following generic custom JsonConverter to deserialize your data:
public class ColumnarDataToListConverter<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)
{
if (reader.TokenType == JsonToken.Null)
return null;
var list = existingValue as List<T> ?? new List<T>();
var obj = JObject.Load(reader);
var columns = obj["COLUMNS"] as JArray;
var data = obj["DATA"] as JArray;
if (data == null)
return list;
list.AddRange(data
.Select(item => new JObject(columns.Zip(item, (c, v) => new JProperty((string)c, v))))
.Select(o => o.ToObject<T>(serializer)));
return list;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it as follows:
var settings = new JsonSerializerSettings { Converters = new[] { new ColumnarDataToListConverter<PackageEntity>() } };
var list = JsonConvert.DeserializeObject<List<PackageEntity>>(responseString, settings);
Note the use of Enumerable.Zip() to pair up the entries in the column array with the entries in each row of the data array into a temporary JObject for deserialization.

JSON serialization with custom mapping in C#

I want to serialize/deserialize following classes into/from JSON:
public class Employee
{
string name;
Position position;
}
public class Position
{
string positionName;
int salary;
}
The tricky part is that I want to treat Position fields as Employee fields, so JSON would look like this:
{
"name": "John",
"positionName": "Manager",
"salary" : 1000
}
How to achieve this using Json.NET ?
You have either to deserialize it as anonymous object either (recommended ) implement a custom deserialization as stated here:
Merge two objects during serialization using json.net?
Please let us know if there any more questions.
Here's an example (you can find it in the provided link):
public class FlattenJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}
My solution is this
static void Main(string[] args)
{
Position p = new Position();
p.positionName = "Manager";
p.salary = 1000;
Employee e = new Employee();
e.name = "John";
e.position = p;
ResultJson r = new ResultJson();
r.name = e.name;
r.positionName = e.position.positionName;
r.salary = e.position.salary;
var result = JsonConvert.SerializeObject(r);
Console.WriteLine(result);
Console.ReadLine();
}
}
public class Employee
{
public string name { get; set; }
public Position position { get; set; }
}
public class Position
{
public string positionName { get; set; }
public int salary { get; set; }
}
public class ResultJson
{
public string name { get; set; }
public string positionName { get; set; }
public int salary { get; set; }
}
use seperate model for result
You can use this code with NewtonSoft.Json library
[JsonObject]
public class Employee
{
[JsonProperty("name")]
string name;
[JsonProperty("positionName")]
string positionName;
[JsonProperty("salary")]
int salary;
}
Use One class instead 2, or realize own parser
try in this way.
{
"name": "John",
"position":
[{
"positionName": "Manager",
"salary" : 1000
}]
}

Mapping flat JSON/Dictionary to model (containing sub classes)

I want to turn a flat json string into a model, the destination class has subclasses, and the flat json has all of the sub class objects with prefix; like "{classname}.{property}".
{
"FirstName": "Joey",
"LastName": "Billy",
"EmploymentDetails.JobTitle": "JobTitle",
"EmploymentDetails.StartDate": "2015-01-01T00:00:00",
"ContactDetails.HouseNumberName": "10",
"ContactDetails.Road": "Road"
}
This is my destination class:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual EmploymentDetails EmploymentDetails { get;set;}
public virtual ContactDetails ContactDetails { get;set;}
}
public class EmploymentDetails {
public string JobTitle { get; set; }
public DateTime StartDate { get; set; }
}
public class ContactDetails {
public string HouseNumberName { get; set; }
public string Road { get; set; }
}
I tried the following:
public static void main() {
var json = #"{""FirstName"": ""Joey"",""LastName"": ""Billy"",""EmploymentDetails.JobTitle"": ""JobTitle"",""EmploymentDetails.StartDate"": ""2015-01-01T00:00:00"",""ContactDetails.HouseNumberName"": ""10"",""ContactDetails.Road"": ""Road"",}";
//try using AutoMapper
Mapper.CreateMap<string,Person>();
var personModel = Mapper.Map<Person>(json);
//just returns null values
//try using Newtonsoft
personModel = Newtonsoft.Json.JsonConvert.DeserializeObject<Person>(json);
//fills values but obviously returns no nested data
}
I know that Automapper has RecognizePrefix and RecognizeDestinationPrefix, but AutoMapper seems to only care if it's in the orignal object, and not a sub class.
Potentially I could take my JSON string and make it a Dictionary, but even then I don't know how to map it to a model with sub classes.
There was hope that I could have an infinite amount of sub classes and the JSON string could just map the flat JSON model to a model.
You can make a JsonConverter that does this in a generic way, using a ContractResolver to group and populate properties in the class being deserialized or its contained classes as appropriate.
You didn't ask for serialization, only deserialization, so that's what this does:
public class JsonFlatteningConverter : JsonConverter
{
readonly IContractResolver resolver;
public JsonFlatteningConverter(IContractResolver resolver)
{
if (resolver == null)
throw new ArgumentNullException();
this.resolver = resolver;
}
public override bool CanConvert(Type objectType)
{
return resolver.ResolveContract(objectType) is JsonObjectContract;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
var contract = (JsonObjectContract)resolver.ResolveContract(objectType); // Throw an InvalidCastException if this object does not map to a JObject.
existingValue = existingValue ?? contract.DefaultCreator();
if (jObject.Count == 0)
return existingValue;
var groups = jObject.Properties().GroupBy(p => p.Name.Contains('.') ? p.Name.Split('.').FirstOrDefault() : null).ToArray();
foreach (var group in groups)
{
if (string.IsNullOrEmpty(group.Key))
{
var subObj = new JObject(group);
using (var subReader = subObj.CreateReader())
serializer.Populate(subReader, existingValue);
}
else
{
var jsonProperty = contract.Properties[group.Key];
if (jsonProperty == null || !jsonProperty.Writable)
continue;
if (jsonProperty != null)
{
var subObj = new JObject(group.Select(p => new JProperty(p.Name.Substring(group.Key.Length + 1), p.Value)));
using (var subReader = subObj.CreateReader())
{
var propertyValue = serializer.Deserialize(subReader, jsonProperty.PropertyType);
jsonProperty.ValueProvider.SetValue(existingValue, propertyValue);
}
}
}
}
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then use it thusly:
var resolver = new DefaultContractResolver();
var settings = new JsonSerializerSettings { ContractResolver = resolver, Converters = new JsonConverter[] { new JsonFlatteningConverter(resolver) } };
var person = JsonConvert.DeserializeObject<Person>(json, settings);
Prototype fiddle.

Categories

Resources