Deserializing class with abstract property JSON - json

public abstract class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
[JsonProperty(Required = Required.Always)]
public string Type { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
I already have a JSON converter working to deserialize such objects based on the value of the Type property.
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
Type t = typeof(T);
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class PersonConverter : JsonCreationConverter<Person>
{
protected override Person Create(Type objectType, JObject jObject)
{
if(jObject["Type"] == null)
{
throw new ArgumentException();
}
string type = (string)jObject["Type"];
if(type == null)
{
throw new ArgumentException();
}
if(type.Equals("Employee", StringComparison.InvariantCultureIgnoreCase))
{
return new Employee();
}
else if (type.Equals("Artist", StringComparison.InvariantCultureIgnoreCase))
{
return new Artist();
}
return null;
}
}
string json = "[{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]";
List<Person> person = JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());
The above works well.
Now, I have the following class:
public class City
{
public string Name { get; set; }
public int Population { get; set; }
public Person[] Persons { get; set; }
}
How do I write a converter for this City class that can use the PersonConverter to initialize the Persons property? My initial thought was to extract the Persons part as JObject, and then call Deserialize on it using PersonConverter in its ReadJson method, similar to the sample below.
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
But ReadJson throws an exception in the Serializer.Populate method since abstract classes cannot be instantiated.
The following is the JSON string as an example:
string Cityjson = "{\"Name\": \"London\" , \"Population\": \"1000\" , \"Persons\": [{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]}";
Approach #1
I solved this by
Marking the Persons property to be ignored in deserialization
[JsonIgnore]
public Person[] Persons { get; set; }
In the Create method, instantiating the City object and using PersonConverter to initialize the Persons property
protected override City Create(Type objectType, JObject jObject)
{
if (jObject["Persons"] == null)
{
throw new ArgumentException();
}
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
var city = new City();
city.Persons = persons.ToArray();
return city;
}
The ReadJson method would populate the remaining City properties as usual.
Are there any other approaches?

I think this is the most suitable way
In ReadJson when array is passed it was basically crashing since Jarray is not jboject. So, I updated the ReadJson as follows and it worked.
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
JArray jObject = JArray.Load(reader);
List<T> list = new List<T>();
for (int i = 0; i < jObject.Count(); i++)
{
var p = jObject[i];
JObject ob = p as JObject;
T value = Create(objectType, ob);
serializer.Populate(ob.CreateReader(), value);
list.Add(value);
}
return list.ToArray();
}
else
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
And yes, I don't need a CityConverter. Adding PersonConverter is enough.

Related

Is it possible to tell Newtonsoft to map a declared $type to a different type when deserialising? [duplicate]

Suppose I have the following class hierarchy:
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
Is it possible to have json.net use property (say "type" or "discriminator") to determine which type the object when it deserializes the organization? For example, the following should deserialize an instance of Company.
{
"type": "company"
/* other properties related to companies */
}
And the following should deserialize an instance of NonProfitOrganization.
{
"type": "non-profit"
/* other properties related to non profit */
}
When I call the following:
Organization organization = JsonConvert.DeserializeObject<Organization>(payload);
where payload is the above JSON snippets. I had a look at setting the "TypeNameHandling" on properties or classes but it serializes the whole .NET type, which isn't "portable" between the client and server when the classes are defined in different namespaces and assemblies.
I'd rather define the type is a neutral manner which clients written in any language can use to determine the actual type of the object type being serialized.
In case you are still looking, here is an example: http://james.newtonking.com/archive/2011/11/19/json-net-4-0-release-4-bug-fixes.aspx
This will allow you to create a table based mapping:
public class TypeNameSerializationBinder : SerializationBinder
{
public TypeNameSerializationBinder(Dictionary<Type, string> typeNames = null)
{
if (typeNames != null)
{
foreach (var typeName in typeNames)
{
Map(typeName.Key, typeName.Value);
}
}
}
readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public void Map(Type type, string name)
{
this.typeToName.Add(type, name);
this.nameToType.Add(name, type);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
var name = typeToName.Get(serializedType);
if (name != null)
{
assemblyName = null;
typeName = name;
}
else
{
assemblyName = serializedType.Assembly.FullName;
typeName = serializedType.FullName;
}
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == null)
{
var type = this.nameToType.Get(typeName);
if (type != null)
{
return type;
}
}
return Type.GetType(string.Format("{0}, {1}", typeName, assemblyName), true);
}
}
The code has a slight defect in that if a type name mapping is attempted where the type is unique but the name is already used, the Map method will throw an exception after the type-to-name mapping is already added leaving the table in an inconsistent state.
To take eulerfx's answer further; I wanted to apply DisplayName attribute to a class and have that automatically become the type name used; to that end:
public class DisplayNameSerializationBinder : DefaultSerializationBinder
{
private Dictionary<string, Type> _nameToType;
private Dictionary<Type, string> _typeToName;
public DisplayNameSerializationBinder()
{
var customDisplayNameTypes =
this.GetType()
.Assembly
//concat with references if desired
.GetTypes()
.Where(x => x
.GetCustomAttributes(false)
.Any(y => y is DisplayNameAttribute));
_nameToType = customDisplayNameTypes.ToDictionary(
t => t.GetCustomAttributes(false).OfType<DisplayNameAttribute>().First().DisplayName,
t => t);
_typeToName = _nameToType.ToDictionary(
t => t.Value,
t => t.Key);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (false == _typeToName.ContainsKey(serializedType))
{
base.BindToName(serializedType, out assemblyName, out typeName);
return;
}
var name = _typeToName[serializedType];
assemblyName = null;
typeName = name;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (_nameToType.ContainsKey(typeName))
return _nameToType[typeName];
return base.BindToType(assemblyName, typeName);
}
}
and usage example:
public class Parameter
{
public string Name { get; set; }
};
[DisplayName("bool")]
public class BooleanParameter : Parameter
{
}
[DisplayName("string")]
public class StringParameter : Parameter
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
}
[DisplayName("number")]
public class NumberParameter : Parameter
{
public double Min { get; set; }
public double Max { get; set; }
public string Unit { get; set; }
}
[DisplayName("enum")]
public class EnumParameter : Parameter
{
public string[] Values { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var parameters = new Parameter[]
{
new BooleanParameter() {Name = "alive"},
new StringParameter() {Name = "name", MinLength = 0, MaxLength = 10},
new NumberParameter() {Name = "age", Min = 0, Max = 120},
new EnumParameter() {Name = "status", Values = new[] {"Single", "Married"}}
};
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Binder = new DisplayNameSerializationBinder(),
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var json = JsonConvert.SerializeObject(parameters);
var loadedParams = JsonConvert.DeserializeObject<Parameter[]>(json);
Console.WriteLine(JsonConvert.SerializeObject(loadedParams));
}
}
output:
[
{
"$type": "bool",
"name": "alive"
},
{
"$type": "string",
"maxLength": 10,
"name": "name"
},
{
"$type": "number",
"max": 120.0,
"name": "age"
},
{
"$type": "enum",
"values": [
"Single",
"Married"
],
"name": "status"
}
]
I've written purely declarative solution with ability to specify custom discriminator field, and provide scoped name handling per base class (as opposed to usecure global JsonSerializationSettings, especially on different Web-Api when we do not have ability to specify custom JsonSerializationSettings).
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
// Discriminated Json Converter (JsonSubtypes) implementation for .NET
//
// MIT License
//
// Copyright (c) 2016 Anatoly Ressin
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////// USAGE ////////////////////////////////////////////////////////////////////////////////
[JsonConverter(typeof(JsonSubtypes))] // Discriminated base class SHOULD NOT be abstract
public class ShapeBase {
[JsonTag, JsonProperty("#type")] // it SHOULD contain a property marked with [JsonTag]
public string Type {get;set;} // only one [JsonTag] annotation allowed per discriminated class
// it COULD contain other properties, however this is NOT RECOMMENDED
// Rationale: instances of this class will be created at deserialization
// only for tag sniffing, and then thrown away.
}
public abstract class Shape: ShapeBase { // If you want abstract parent - extend the root
public abstract double GetArea(); // with needed abstract stuff, then use this class everywhere (see DEMO below)
}
[JsonSubtype("circle")] // Every final class-case SHOULD be marked with [JsonSubtype(tagValue)]
public class Circle: Shape { // Two disctinct variant classes MUST have distinct tagValues
[JsonProperty("super-radius")] // You CAN use any Json-related annotation as well
public double Radius { get; set; }
public override double GetArea() {
return Radius * Radius * Math.PI;
}
}
[JsonSubtype("rectangle")]
public class Rectangle: Shape {
public double Height { get; set; }
public double Width { get; set; }
public override double GetArea() {
return Width * Height;
}
}
[JsonSubtype("group")]
public class Group: Shape {
[JsonProperty("shapes")]
public List<Shape> Items { get; set; }
public override double GetArea() {
return Items.Select(item => item.GetArea()).Sum();
}
}
// Every final class-case SHOULD be registered with JsonSubtypes.register(typeof(YourConcreteClass))
// either manually or with auto-register capability:
// You can auto-register all classes marked with [JsonSubtype(tag)] in given Assembly
// using JsonSubtypes.autoRegister(yourAssembly)
////////////////// DEMO /////////////////////////////////////////////////////////////////////////////////
public class Program
{
public static void Main()
{
JsonSubtypes.autoRegister(Assembly.GetExecutingAssembly());
Shape original = new Group() {
Items = new List<Shape> {
new Circle() { Radius = 5 },
new Rectangle() { Height = 10, Width = 20 }
}
};
string str = JsonConvert.SerializeObject(original);
Console.WriteLine(str);
var copy = JsonConvert.DeserializeObject(str,typeof(Shape)) as Shape;
// Note: we can deserialize object using any class from the hierarchy.
// Under the hood, anyway, it will be deserialized using the top-most
// base class annotated with [JsonConverter(typeof(JsonSubtypes))].
// Thus, only soft-casts ("as"-style) are safe here.
Console.WriteLine("original.area = {0}, copy.area = {1}", original.GetArea(), copy.GetArea());
}
}
//////////////////////// IMPLEMENTATION //////////////////////////////////////////////////////////////////
public class JsonSubtypeClashException: Exception {
public string TagValue { get; private set;}
public Type RootType { get; private set; }
public Type OldType { get; private set; }
public Type NewType { get; private set; }
public JsonSubtypeClashException(Type rootType, string tagValue, Type oldType, Type newType): base(
String.Format(
"JsonSubtype Clash for {0}[tag={1}]: oldType = {2}, newType = {3}",
rootType.FullName,
tagValue,
oldType.FullName,
newType.FullName
)
) {
TagValue = tagValue;
RootType = rootType;
OldType = oldType;
NewType = newType;
}
}
public class JsonSubtypeNoRootException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoRootException(Type subType): base(
String.Format(
"{0} should be inherited from the class with the [JsonConverter(typeof(JsonSubtypes))] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNoTagException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoTagException(Type subType): base(
String.Format(
#"{0} should have [JsonSubtype(""..."")] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNotRegisteredException: Exception {
public Type Root { get; private set; }
public string TagValue { get; private set; }
public JsonSubtypeNotRegisteredException(Type root, string tagValue): base(
String.Format(
#"Unknown tag={1} for class {0}",
root.FullName,
tagValue
)
) {
Root = root;
TagValue = tagValue;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class JsonSubtypeAttribute: Attribute {
private string tagValue;
public JsonSubtypeAttribute(string tagValue) {
this.tagValue = tagValue;
}
public string TagValue {
get {
return tagValue;
}
}
}
public static class JsonSubtypesExtension {
public static bool TryGetAttribute<T>(this Type t, out T attribute) where T: Attribute {
attribute = t.GetCustomAttributes(typeof(T), false).Cast<T>().FirstOrDefault();
return attribute != null;
}
private static Dictionary<Type, PropertyInfo> tagProperties = new Dictionary<Type, PropertyInfo>();
public static bool TryGetTagProperty(this Type t, out PropertyInfo tagProperty) {
if (!tagProperties.TryGetValue(t, out tagProperty)) {
JsonConverterAttribute conv;
if (t.TryGetAttribute(out conv) && conv.ConverterType == typeof(JsonSubtypes)) {
var props = (from prop in t.GetProperties() where prop.GetCustomAttribute(typeof(JsonTagAttribute)) != null select prop).ToArray();
if (props.Length == 0) throw new Exception("No tag");
if (props.Length > 1) throw new Exception("Multiple tags");
tagProperty = props[0];
} else {
tagProperty = null;
}
tagProperties[t] = tagProperty;
}
return tagProperty != null;
}
public static bool TryGetTagValue(this Type t, out string tagValue) {
JsonSubtypeAttribute subtype;
if (t.TryGetAttribute(out subtype)) {
tagValue = subtype.TagValue;
return true;
} else {
tagValue = null;
return false;
}
}
public static bool TryGetJsonRoot(this Type t, out Type root, out PropertyInfo tagProperty) {
root = t;
do {
if (root.TryGetTagProperty(out tagProperty)) {
return true;
}
root = root.BaseType;
} while (t != null);
return false;
}
}
public class JsonTagAttribute: Attribute {
}
public class JsonTagInfo {
public PropertyInfo Property { get; set; }
public string Value { get; set; }
}
public class JsonRootInfo {
public PropertyInfo Property { get; set; }
public Type Root { get; set; }
}
public abstract class DefaultJsonConverter: JsonConverter {
[ThreadStatic]
private static bool silentWrite;
[ThreadStatic]
private static bool silentRead;
public sealed override bool CanWrite {
get {
var canWrite = !silentWrite;
silentWrite = false;
return canWrite;
}
}
public sealed override bool CanRead {
get {
var canRead = !silentRead;
silentRead = false;
return canRead;
}
}
protected void _WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
silentWrite = true;
serializer.Serialize(writer, value);
}
protected Object _ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
silentRead = true;
return serializer.Deserialize(reader, objectType);
}
}
public class JsonSubtypes: DefaultJsonConverter {
private static Dictionary<Type, Dictionary<string, Type>> implementations = new Dictionary<Type, Dictionary<string, Type>>();
private static Dictionary<Type, JsonTagInfo> tags = new Dictionary<Type, JsonTagInfo>();
private static Dictionary<Type, JsonRootInfo> roots = new Dictionary<Type, JsonRootInfo>();
public static void register(Type newType) {
PropertyInfo tagProperty;
Type root;
if (newType.TryGetJsonRoot(out root, out tagProperty)) {
for(var t = newType; t != root; t = t.BaseType) {
roots[t] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
}
roots[root] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
Dictionary<string, Type> implementationMap;
if (!implementations.TryGetValue(root, out implementationMap)) {
implementationMap = new Dictionary<string, Type>();
implementations[root] = implementationMap;
}
JsonSubtypeAttribute attr;
if (!newType.TryGetAttribute(out attr)) {
throw new JsonSubtypeNoTagException(newType);
}
var tagValue = attr.TagValue;
Type oldType;
if (implementationMap.TryGetValue(tagValue, out oldType)) {
throw new JsonSubtypeClashException(root, tagValue, oldType, newType);
}
implementationMap[tagValue] = newType;
tags[newType] = new JsonTagInfo() {
Property = tagProperty,
Value = tagValue
};
} else {
throw new JsonSubtypeNoRootException(newType);
}
}
public static void autoRegister(Assembly assembly) {
foreach(var type in assembly.GetTypes().Where(type => type.GetCustomAttribute<JsonSubtypeAttribute>() != null)) {
register(type);
}
}
public override bool CanConvert(Type t) {
return true;
}
public static T EnsureTag<T>(T value) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(value.GetType(), out tagInfo)) {
tagInfo.Property.SetValue(value, tagInfo.Value);
}
return value;
}
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
_WriteJson(writer, EnsureTag(value), serializer);
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(objectType, out tagInfo)) {
return _ReadJson(reader, objectType, existingValue, serializer);
} else {
JsonRootInfo rootInfo;
if (roots.TryGetValue(objectType, out rootInfo)) {
JToken t = JToken.ReadFrom(reader);
var stub = _ReadJson(t.CreateReader(), rootInfo.Root, existingValue, serializer);
var tagValue = rootInfo.Property.GetValue(stub) as string;
var implementationMap = implementations[rootInfo.Root];
Type implementation;
if (implementationMap.TryGetValue(tagValue, out implementation)) {
return ReadJson(t.CreateReader(), implementation, null, serializer);
} else {
throw new JsonSubtypeNotRegisteredException(rootInfo.Root, tagValue);
}
} else {
return _ReadJson(reader, objectType, existingValue, serializer);
}
}
}
public static T Deserialize<T>(string s) where T: class {
return JsonConvert.DeserializeObject(s, typeof(T)) as T;
}
public static string Serialize<T>(T value) where T: class {
return JsonConvert.SerializeObject(value);
}
}
output:
{"shapes":[{"super-radius":5.0,"#type":"circle"},{"Height":10.0,"Width":20.0,"#type":"rectangle"}],"#type":"group"}
original.area = 278.539816339745, copy.area = 278.539816339745
You can grab it here:
https://dotnetfiddle.net/ELcvnk
With another JsonSubtypes converter implementation.
Usage:
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
[TestMethod]
public void Demo()
{
var input = #"{""Sound"":""Bark"",""Breed"":""Jack Russell Terrier""}"
var animal = JsonConvert.DeserializeObject<Animal>(input);
Assert.AreEqual("Jack Russell Terrier", (animal as Dog)?.Breed);
}
the converter implementation can be directly downloaded from the repository: JsonSubtypes.cs and is also availble as a nuget package
Use this JsonKnownTypes, it's very similar way to use, add couple of attribute:
[JsonConverter(typeof(JsonKnownTypeConverter<Organization>))]
[JsonDiscriminator(Name = "discriminator")]
[JsonKnownType(typeof(Company), "company")]
[JsonKnownType(typeof(NonProfitOrganization), "non-profit")]
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
And serialize:
var json = JsonConvert.SerializeObject(youObject)
Output json:
{..., "discriminator":"non-profit"} //if object was NonProfitOrganization
Deserialization:
var organization = JsonConvert.DeserializeObject<Organization>(payload);

.NET NewtonSoft JSON deserialize with different property name [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable

How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable
my result from serialization
{"images":[]} or {"images":{}}
and would like to get
{"images":null}
my simple class
public class Images
{
[JsonProperty("images", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Images()
{
Parameters = new Parameters(); **// I know I could not initiate here only when I need it but I don't want to do it**
}
[JsonProperty("parameters", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Parameters Parameters { get; set; }
}
public class Parameters
{
[JsonProperty("id", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)]
public string Id { get; set; }
}
I did something like that
internal class NullEmptyConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(GuidId);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
return value;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "[]")
{
serializer.Serialize(writer, null);
return;
}
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "{}")
{
serializer.Serialize(writer, null);
return;
}
serializer.Serialize(writer,untypedValue);
return;
}
}
use
   
[JsonConverter (typeof (NullEmptyConverter))]
immediately before
public Parameters Parameters { get; set; }
It works, but maybe it can be done better, simpler or somewhere else something is wrong here?

Deserialize an object into another similar object of another type json.net

I have a question similar to Cannot deserialize JSON array into type - Json.NET, but I still get errors.
So, I have 3 classes:
public class Class1
{
public string[] P2 { get; set; }
}
public class Class2
{
public Wrapper<string>[] P2 { get; set; }
}
public class Wrapper<T>
{
public T Value { get; set; }
}
I am trying to serialize Class 2 into string and back into Class 1.
Here's how:
Class2 c2 = new Class2
{
P2 = new Wrapper<string>[]
{
new Wrapper<string> { Value = "a" },
new Wrapper<string> { Value = "a" },
},
};
string s = JsonConvert.SerializeObject(c2);
Class1 c1 = (Class1)JsonConvert.DeserializeObject(s, typeof(Class1), new FormatConverter());
FormatConverter class is defined below:
public class FormatConverter : 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)
{
if (objectType == typeof(string[]))
{
List<string> list = new List<string>();
while (reader.Read())
{
if (reader.TokenType != JsonToken.StartObject)
{
continue;
}
Wrapper<string> obj = (Wrapper<string>)serializer.Deserialize(reader, typeof(Wrapper<string>));
list.Add(obj.Value);
}
return list.ToArray();
}
}
public override bool CanConvert(Type type)
{
if (type == typeof(string[]))
{
return true;
}
return false;
}
}
What am I missing? I get following exception:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Unexpected end when deserializing object. Path '', line 1, position 46.
Thanks,
Alex
I managed to find the answer myself. Here's how:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (type == typeof(string[]) && reader.TokenType == JsonToken.StartArray)
{
//remove converter not to trigger a recursive call
var converter = serializer.Converters[0];
serializer.Converters.RemoveAt(0);
//deserialization into correct type
Wrapper<string>[] obj = (Wrapper<string>[])serializer.Deserialize(reader, typeof(Wrapper<string>[]));
//restore converter
serializer.Converters.Add(converter);
if (obj != null)
{
return obj.Select(w => w == null ? null : w.Value).ToArray();
}
return reader.Value;
}
}

Web API - JSON serialize properties as array of strings

I have a class
public class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
I'd like to serialize it into:
{ "Person": "[John, Smith, 13]" }
instead of
{"Name":"John", "LastName":"Smith", "Age": 13}
Is it possible to achieve this without writing a custom serializer? If not, how do I apply a custom serializer only to a specific class?
Since the json string you want to get when serializing an instance of Person is not the standard one, you'll need to write a custom converter and decorate your class with it. One way to do that is the following. Write class that inherits from JsonConverter:
public class CustomConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null) return;
writer.WriteStartObject();
writer.WritePropertyName(value.GetType().Name);
writer.WriteStartArray();
var properties = value.GetType().GetProperties();
foreach (var property in properties)
writer.WriteValue(value.GetType().GetProperty(property.Name).GetValue(value));
writer.WriteEndArray();
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Person);
}
}
Decorate your class with the JsonConverter attribute:
[JsonConverter(typeof(CustomConverter))]
public class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Now lets say that you use the following code:
var person = new Person
{
Name = "John",
LastName = "Smith",
Age = 13
};
var json = JsonConvert.SerializeObject(person, Formatting.Indented);
The json varible wil have this value:
{
"Person": [
"John",
"Smith",
13
]
}
Demo here