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

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.

Related

JsonConvert List to Json Properties

I would want to convert an object with a list property into a json object. The list property should not be parsed directly, instead the items in the list should be added to the json object as properties. The property names should be custom and have the index as suffix. Only the serialzation is needed.
Example:
class Foo{
public bool Boolean { get; set; }
public List<Bar> List { get; set; }
// 50+ additional properties to be serialized.
}
class Bar{
public string Value{ get; set; }
}
Code:
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
JsonConvert.Serialize(bar)
Desired output:
{
"Boolean": true,
"property1" : "Foo1",
"property2" : "Bar2",
"property3" : "Foo3"
}
Note that, in my real classes, there are over 50 other properties for Foo so practically I cannot write a JsonConverter<Foo> that serializes each one manually.
Here is one other approach to produce expected JSON without JSON converter
Redefine the POCO object as below
public class Foo
{
public bool Boolean;
[JsonIgnore]
public List<Bar> List;
[JsonExtensionData]
public Dictionary<string, JToken> Values
{
get
{
return List.Select((v, i) => new KeyValuePair<string, JToken>($"Property{i + 1}", JValue.CreateString(v.Value))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
}
public class Bar{
public string Value{ get; set; }
}
Then use JSON library to serialize as below
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
var json = JsonConvert.SerializeObject(bar, Formatting.Indented);
Console.WriteLine(json);
Sample fiddle: https://dotnetfiddle.net/jgWlLB
You can generate the JSON you require with a custom JsonConverter. Since your "real" data model has 50+ properties, you can use Json.NET's own contract metadata to loop through the properties of Foo and serialize them appropriately. The following does the trick:
public class ItemListContainerConverter<TContainer, TItem> : JsonConverter<TContainer>
{
protected virtual string PropertyName => "property";
public override void WriteJson(JsonWriter writer, TContainer value, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException("value is not a JSON object");
JsonProperty itemListProperty = null;
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
{
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && (serializer.NullValueHandling == NullValueHandling.Ignore || property.NullValueHandling == NullValueHandling.Ignore))
continue;
if (typeof(IEnumerable<TItem>).IsAssignableFrom(property.PropertyType))
{
itemListProperty = (itemListProperty == null ? property : throw new JsonSerializationException("Too many IEnumerable<Bar> properties"));
}
else
{
writer.WritePropertyName(property.PropertyName);
if (propertyValue == null)
writer.WriteNull();
else if (property.Converter != null && property.Converter.CanWrite)
property.Converter.WriteJson(writer, propertyValue, serializer);
else
serializer.Serialize(writer, propertyValue);
}
}
if (itemListProperty != null)
{
var itemList = itemListProperty.ValueProvider.GetValue(value) as IEnumerable<TItem>;
if (itemList != null)
{
var itemContract = serializer.ContractResolver.ResolveContract(typeof(TItem));
var valueMethod = GetItemValueMethod(itemContract);
int i = 1;
foreach (var item in itemList)
{
writer.WritePropertyName(PropertyName + (i++).ToString(CultureInfo.InvariantCulture));
serializer.Serialize(writer, valueMethod(item));
}
}
}
writer.WriteEndObject();
}
protected virtual Func<object, object> GetItemValueMethod(JsonContract itemContract)
{
if (!(itemContract is JsonObjectContract objectContract))
throw new JsonSerializationException("item contract is not a JsonObjectContract");
var property = objectContract.Properties.Single(p => ShouldSerialize(p));
return (o) => property.ValueProvider.GetValue(o);
}
protected virtual bool ShouldSerialize(JsonProperty property) => property.Readable && !property.Ignored;
protected virtual bool ShouldSerialize(JsonProperty property, object value) => ShouldSerialize(property) && (property.ShouldSerialize == null || property.ShouldSerialize(value));
public override TContainer ReadJson(JsonReader reader, Type objectType, TContainer existingValue, bool hasExistingValue, JsonSerializer serializer) =>
// Not requested to be implemnented as per the question.
throw new NotImplementedException();
}
And then you can serialize your model Foo as follows:
var settings = new JsonSerializerSettings
{
Converters = { new ItemListContainerConverter<Foo, Bar>() }
};
var json = JsonConvert.SerializeObject(foo, Formatting.Indented, settings);
Which results in
{
"Boolean": true,
"property1": "Foo1",
"property2": "Bar2",
"property3": "Foo3"
}
Notes:
This solution does not require serialization to an intermediate JToken representation.
The converter ItemListContainerConverter<TContainer, TItem> assumes that the container type TContainer will be serialized as a JSON object that has a single property of type IEnumerable<TItem> and any number of additional properties. The additional properties will be serialized normally while the items of the IEnumerable<TItem> collection will be serialized as "propertyI" properties as required.
It assumes that the item type TItem has a single serializable property. If your TItem has more than one property, you can override GetItemValueMethod() to select the property you wish to serialize as the value of the "propertyI" properties.
Deserialization was not implemented as it was not requested in the question.
Demo fiddle here.
Classes:
public class Foo
{
public bool Boolean;
[JsonIgnore]
public List<Bar> List;
}
class Bar{
string Value{ get; set; }
}
public class FooConverter : JsonConverter<Foo>
{
public override Foo ReadJson(JsonReader reader, Type objectType, Foo existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, Foo value, JsonSerializer serializer)
{
var t = JToken.FromObject(value);
JObject o = (JObject)t;
for (int i = 0; i < value.List.Count; i++)
{
o.Add($"prop{i + 1}", new JValue(value.List[i].Value));
}
o.WriteTo(writer);
}
}
Code:
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
JsonConvert.SerializeObject(bar, Formatting.Indented, new FooConverter())
My earlier attempts failed because i tried the simple approach of just passing bar to SerialzeObjects and adding the JsonConverter attribute to the Foo class and so it kept looping on itself.
try this
JObject o = (JObject)JToken.FromObject(bar);
var i = 0;
foreach (var item in (JArray)o["List"])
{
i++;
o.Add("property" + i.ToString(), item["Value"]);
}
o.Remove("List");
o.ToString();
result
{
"Boolean": true,
"property1": "Foo1",
"property2": "Bar2",
"property3": "Foo3"
}

Json.Net: How to ignore null elements in array deserializing a JSON

I have this JSON:
{
"Variable1": "1",
"Variable2": "50000",
"ArrayObject": [null]
}
I have this stubs:
public class Class1
{
public string Variable1 { get; set; }
public string Variable2 { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<ArrayObject> ArrayObject { get; set; }
}
public class ArrayObject
{
public string VariableArray1 { get; set; }
public string VariableArray2 { get; set; }
}
I'd like to ignore the null elements inside array preferably using the json settings or some sort of converter. So the result should be an empty array in that case or null.
Here is the code I've been trying to make this work.
class Program
{
static void Main(string[] args)
{
string json = #"{
""Variable1"": ""1"",
""Variable2"": ""50000"",
""ArrayObject"": [null]
}";
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
};
Class1 class1 = JsonConvert.DeserializeObject<Class1>(json, settings);
Console.WriteLine(class1.ArrayObject == null);
Console.WriteLine(class1.ArrayObject.Count());
foreach (var item in class1.ArrayObject)
{
Console.WriteLine(item.VariableArray1);
Console.WriteLine(item.VariableArray2);
Console.WriteLine("#######################");
}
}
public class Class1
{
public string Variable1 { get; set; }
public string Variable2 { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<ArrayObject> ArrayObject { get; set; }
}
public class ArrayObject
{
public string VariableArray1 { get; set; }
public string VariableArray2 { get; set; }
}
}
I thought that using NullValueHandling = NullValueHandling.Ignore would make it work. Apparently not. Any ideas?
Update: I need a global solution, I don't want to have to modify every viewmodel inside my project.
Setting NullValueHandling = NullValueHandling.Ignore will not filter null values from JSON arrays automatically during deserialization because doing so would cause the remaining items in the array to be re-indexed, rendering invalid any array indices that might have been stored elsewhere in the serialization graph.
If you don't care about preserving array indices and want to filter null values from the array during deserialization anyway, you will need to implement a custom JsonConverter such as the following:
public class NullFilteringListConverter<T> : JsonConverter<List<T>>
{
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
var list = existingValue as List<T> ?? (List<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
serializer.Populate(reader, list);
list.RemoveAll(i => i == null);
return list;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) => throw new NotImplementedException();
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
And apply it to your model as follows:
public class Class1
{
public string Variable1 { get; set; }
public string Variable2 { get; set; }
[JsonConverter(typeof(NullFilteringListConverter<ArrayObject>))]
public List<ArrayObject> ArrayObject { get; set; }
}
Or, add it in settings as follows:
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
Converters = { new NullFilteringListConverter<ArrayObject>() },
};
Notes:
Since you didn't ask about filtering null values during serialization, I didn't implement it, however it would be easy to do by changing CanWrite => true; and replacing WriteJson() with:
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) => serializer.Serialize(writer, value.Where(i => i != null));
Demo fiddles here and here.
Update
I need a global solution. If you need to automatically filter all null values from all possible List<T> objects in every model, the following JsonConverter will do the job:
public class NullFilteringListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType.IsArray || objectType == typeof(string) || objectType.IsPrimitive)
return false;
var itemType = objectType.GetListItemType();
return itemType != null && (!itemType.IsValueType || Nullable.GetUnderlyingType(itemType) != null);
}
object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var list = existingValue as List<T> ?? (List<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
serializer.Populate(reader, list);
list.RemoveAll(i => i == null);
return list;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
var itemType = objectType.GetListItemType();
var method = typeof(NullFilteringListConverter).GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
try
{
return method.MakeGenericMethod(new[] { itemType }).Invoke(this, new object[] { reader, objectType, existingValue, serializer });
}
catch (Exception ex)
{
// Wrap the TargetInvocationException in a JsonSerializerException
throw new JsonSerializationException("Failed to deserialize " + objectType, ex);
}
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
public static partial class JsonExtensions
{
internal static Type GetListItemType(this Type type)
{
// Quick reject for performance
if (type.IsPrimitive || type.IsArray || type == typeof(string))
return null;
while (type != null)
{
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(List<>))
return type.GetGenericArguments()[0];
}
type = type.BaseType;
}
return null;
}
}
And add it to settings as follows:
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
Converters = { new NullFilteringListConverter() },
};
Class1 class1 = JsonConvert.DeserializeObject<Class1>(json, settings);
With this converter, adding [JsonConverter(typeof(NullFilteringListConverter<ArrayObject>))] to ArrayObject is no longer required. Do note that all List<T> instances in your deserialization graph may get re-indexed whenever these settings are used! Make sure you really want this as the side-effects of changing indices of items referred to elsewhere by index may include data corruption (incorrect references) rather than an outright ArgumentOutOfRangeException.
Demo fiddle #3 here.
You could also have a custom setter that filters out null values.
private List<ArrayObject> _arrayObject;
public List<ArrayObject> ArrayObject
{
get => _arrayObject;
set
{
_arrayObject = value.Where(x => x != null).ToList();
}
}
Fiddle working here https://dotnetfiddle.net/ePp0A2
NullValueHandling.Ignore does not filter null values from an array. But you can do this easily by adding a deserialization callback method in your class to filter out the nulls:
public class Class1
{
public string Variable1 { get; set; }
public string Variable2 { get; set; }
public List<ArrayObject> ArrayObject { get; set; }
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
ArrayObject?.RemoveAll(o => o == null);
}
}
Working demo: https://dotnetfiddle.net/v9yn7j

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

ViewModel Custom JsonConverter

I'm trying to create a custom JsonConverter that changes C# property names to camel case and javascript/json property names to pascal case. I feel like I'm on the right track but I'm having trouble understanding what I need to do (and I'm in a time crunch).
I realize I can add the JsonProperty attribute to my C# properties but I would prefer to apply an attribute to the class rather than each property.
public class ViewModelJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var model = JObject.Load(reader);
var properties = model.Properties();
foreach (var prop in properties)
{
RenameToPascalCase(prop.Name, prop.Value);
}
return model;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var model = (JObject)JToken.FromObject(value);
var properties = model.Properties();
foreach (var prop in properties)
{
RenameToCamelCase(prop.Name, prop.Value);
}
}
private void RenameToCamelCase(string name, JToken value)
{
var parent = value.Parent;
if (parent == null)
throw new InvalidOperationException("The parent is missing.");
var newProperty = new JProperty(ToCamelCase(name), value);
parent.Replace(newProperty);
}
private void RenameToPascalCase(string name, JToken value)
{
var parent = value.Parent;
if (parent == null)
throw new InvalidOperationException("The parent is missing.");
var newProperty = new JProperty(ToPascalCase(name), value);
parent.Replace(newProperty);
}
//Example: propertyName
private string ToCamelCase(string value)
{
if (String.IsNullOrEmpty(value) || Char.IsLower(value, 0))
return value;
return Char.ToLowerInvariant(value[0]) + value.Substring(1);
}
//Example: PropertyName
private string ToPascalCase(string value)
{
if (String.IsNullOrEmpty(value) || Char.IsUpper(value, 0))
return value;
return Char.ToUpperInvariant(value[0]) + value.Substring(1);
}
}
Sample Use
[JsonConverter(typeof(ViewModelJsonConverter))]
public class TestClass {
public string PropertyName { get; set; }
}
If you're using Json.Net 9.0.1 or later, you can do what you want by using the NamingStrategyType parameter of the [JsonObject] attribute. So, in other words, just mark the classes you want to be camel cased like this:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class TestClass
{
...
}
You don't need a ContractResolver or a custom JsonConverter.
Here is a round-trip demo:
public class Program
{
public static void Main(string[] args)
{
TestClass tc = new TestClass
{
PropertyName = "foo",
AnotherPropertyName = "bar"
};
Console.WriteLine("--- Serialize ---");
string json = JsonConvert.SerializeObject(tc, Formatting.Indented);
Console.WriteLine(json);
Console.WriteLine();
Console.WriteLine("--- Deserialize ---");
TestClass test = JsonConvert.DeserializeObject<TestClass>(json);
Console.WriteLine("PropertyName: " + test.PropertyName);
Console.WriteLine("AnotherPropertyName: " + test.AnotherPropertyName);
}
}
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class TestClass
{
public string PropertyName { get; set; }
public string AnotherPropertyName { get; set; }
}
Output:
--- Serialize ---
{
"propertyName": "foo",
"anotherPropertyName": "bar"
}
--- Deserialize ---
PropertyName: foo
AnotherPropertyName: bar
Have you ever tried any of these contract resolvers for newtonsoft json.
For eg:
var jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, jsonSerializerSettings);
If you wanted to implement your own then please disregard.

How to deserialize an array of an array of objects using Newtonsoft

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());

Categories

Resources