Json.Net Serialize/Deserialize Derived Types

Json.net serialize/deserialize derived types?

If you are storing the type in your text (as you should be in this scenario), you can use the JsonSerializerSettings.

See: how to deserialize JSON into IEnumerable<BaseType> with Newtonsoft JSON.NET

Be careful, though. Using anything other than TypeNameHandling = TypeNameHandling.None could open yourself up to a security vulnerability.

Json.Net deserialize into C# derived class

It looks like the problem might be due to the fact that the $type metadata property is not the first property in the JSON for your control. Normally, Json.Net needs this property to be first in order to recognize it. Try adding

MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead

to your settings when deserializing. That should allow Json.Net to find the $type property later in the JSON.

Deserialize JSON into multiple inherited classes

You could try serializing your objects yourself with Json.NET and then posting the serialized content into DocumentDb. Then, when you need the data, read it back as a json string and use Json.NET again to deserialize.
Json.NET can handle inheritance, so you just have to configure it to be aware of your type hierarchy. Use the TypeNameHandling setting:

http://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};

How do I deserialize a derived class with json.net

Here is yours example: http://dotnetbyexample.blogspot.ru/2012/02/json-deserialization-with-jsonnet-class.html?m=1

You just have to use overloaded method

JsonConvert.DeserializeObject<List<Item>>
(r.EventArgs.Result, params Newtonsoft.Json.JsonConverter[] converters)

And write your converters

JSON.Net Serializing Derived Classes

The issue here is that your Group<K, T> is a collection that also has properties. Since a JSON container can either be an array (with no properties) or an object (with named key/value pairs), a collection with custom properties cannot be mapped automatically to either without data loss. Json.NET (and all other serializers AFAIK) choose to map the items not the custom properties.

You have a couple ways to deal with this:

  1. Write your own custom JsonConverter. You can determine the generic arguments using reflection along the lines of Json.Net returns Empty Brackets.

  2. Mark your Group<K, T> with [JsonObject].

The second option seems simplest, and would look like:

[JsonObject(MemberSerialization = MemberSerialization.OptIn)] // OptIn to omit the properties of the base class,  e.g. Count
class Group<K, T> : ObservableCollection<T>
{
[JsonProperty("Header")]
public K Key { get; set; }

[JsonProperty("Items")]
IEnumerable<T> Values
{
get
{
foreach (var item in this)
yield return item;
}
set
{
if (value != null)
foreach (var item in value)
Add(item);
}
}

public Group(K Header, IEnumerable<T> Items) // Since there is no default constructor, argument names should match JSON property names.
: base(Items)
{
Key = Header;
}
}

Incidentally, you have another problem -- your Ingredient class does not have a default constructor, and its single parameterized throws a NullReferenceException if the line argument is null. In the absence of a default constructor Json.NET will call the single parameterized constructor, mapping JSON object values to constructor arguments by name. Thus, deserialization throws an exception.

You have a few ways to deal with this:

  1. Add a public default constructor.

  2. Add a private default constructor and mark it with [JsonConstructor]:

    [JsonConstructor]
    Ingredient() { }
  3. Add a private default constructor and deserialize with ConstructorHandling.AllowNonPublicDefaultConstructor:

    var settings = new JsonSerializerSettings { ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor };
    var recipe = JsonConvert.DeserializeObject<Recipe>(json, settings);
  4. Add an if (line != null) check in the constructor. (Not really recommended. Instead your constructor should explicitly throw an ArgumentNullException.)

Having done this, you will gt JSON that looks like:

{
"IngredientsWithHeaders": [
{
"Header": "BlankHeader",
"Items": [
{
"Quantity": "3",
"Modifier": null,
"Unit": "tbsp",
"IngredientName": "butter",
"Preparation": null
}
]
}
],
}

Your proposed JSON has an extra level of nesting with

{
"IngredientsWithHeaders": [
{
"Group": {
"Header": "BlankHeader",

This extra "Group" object is unnecessary.

Json.NET - handle unknown derived types when using TypeNameHandling

Here's a custom JsonConverter that seems to solve the problem:

public class DefaultToUnknownConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Animal).IsAssignableFrom(objectType);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}

JObject jObject = JObject.Load(reader);
try
{
// attempt to deserialize to known type
using (JsonReader jObjectReader = CopyReaderForObject(reader, jObject))
{
// create new serializer, as opposed to using the serializer parm, to avoid infinite recursion
JsonSerializer tempSerializer = new JsonSerializer()
{
TypeNameHandling = TypeNameHandling.Objects
};
return tempSerializer.Deserialize<Animal>(jObjectReader);
}
}
catch (JsonSerializationException)
{
// default to Unknown type when deserialization fails
return jObject.ToObject<Unknown>();
}
}

public override bool CanWrite => false;

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

public static JsonReader CopyReaderForObject(JsonReader reader, JToken jToken)
{
// create reader and copy over settings
JsonReader jTokenReader = jToken.CreateReader();
jTokenReader.Culture = reader.Culture;
jTokenReader.DateFormatString = reader.DateFormatString;
jTokenReader.DateParseHandling = reader.DateParseHandling;
jTokenReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jTokenReader.FloatParseHandling = reader.FloatParseHandling;
jTokenReader.MaxDepth = reader.MaxDepth;
jTokenReader.SupportMultipleContent = reader.SupportMultipleContent;
return jTokenReader;
}

Add the converter to the deserializer settings:

var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
Converters = { new DefaultToUnknownConverter() }
};

The DefaultToUnknownConverter class could be made more generic by passing in the json settings, using generics, etc..

Reference for implementing a custom JsonConverter: https://stackoverflow.com/a/21632292/674237

Deserialization with inheritance Newtonsoft JSON.NET not working properly

Json.Net normally expects the $type metadata to be the first property of each object for best efficiency in deserialization. If the $type does not appear first, then Json.Net assumes it isn't there. That is why you are getting different results when the properties are reordered.

Fortunately, Json.Net provides a MetadataPropertyHandling setting to allow it to cope with this situation. If you set MetadataPropertyHandling to ReadAhead it should solve your problem. Note that this will have an impact on performance.

Here is a code sample from the documentation.

string json = @"{
'Name': 'James',
'Password': 'Password1',
'$type': 'MyNamespace.User, MyAssembly'
}";

object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
// $type no longer needs to be first
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
});

User u = (User)o;

Console.WriteLine(u.Name);
// James


Related Topics



Leave a reply



Submit