149

json.net (newtonsoft)
I am looking through the documentation but I can't find anything on this or the best way to do it.

public class Base { public string Name; } public class Derived : Base { public string Something; } JsonConvert.Deserialize<List<Base>>(text); 

Now I have Derived objects in the serialized list. How do I deserialize the list and get back derived types?

3
  • That isn't how the inheritance works. You can specify JsonConvert.Deserialize<Derived>(text); to include the Name field. Since Derived IS A Base (not the other way around), Base doesn't know anything about Derived's definition. Commented Dec 14, 2011 at 23:13
  • 2
    Sorry, clarified a bit. The issue is I have a list which contains both base and derived objects. So I need to figure out how I tell newtonsoft how to deserialize the derived items. Commented Dec 14, 2011 at 23:18
  • I did you solve this. I have the same problem Commented Jul 29, 2012 at 20:18

5 Answers 5

135

You have to enable Type Name Handling and pass that to the (de)serializer as a settings parameter for both Serialize and Deserialize operations.

Base object1 = new Base() { Name = "Object1" }; Derived object2 = new Derived() { Something = "Some other thing" }; List<Base> inheritanceList = new List<Base>() { object1, object2 }; JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; string Serialized = JsonConvert.SerializeObject(inheritanceList, settings); List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings); 

This will result in correct deserialization of derived classes. A drawback to it is that it will name all the objects you are using, as such it will name the list you are putting the objects in.

Sign up to request clarification or add additional context in comments.

6 Comments

+1. I was googling for 30 minutes until I actually found out that you need to use same settings for SerializeObject & DeserializeObject. I assumed it would use $type implicitly if it is there when deserializing, silly me.
TypeNameHandling.Auto will do it too, and is nicer because it doesn't write the instance type name when it matches the type of the field/property, which is often the case for most fields/properties.
This doesn't work when deserialization is performed on another solution/project. On serialization the name of the Solution is embedded within as type: "SOLUTIONNAME.Models.Model". On deserialization on the other solution it will throw "JsonSerializationException: Could not load assembly 'SOLUTIONNAME'.
I never know it could be so easy! I dug through Google and damn near pull all my hair out trying to figure out how to write a custom serializer/deserializer! Then I finally came across this answer, and finally solved it! You're my lifesaver! Thanks trillions!
@Jebathon, you have in both solutions have at least projects with the same name and classes with the same namespace. But better to share the same library
|
63

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 - see "How to configure Json.NET to create a vulnerable web API".

6 Comments

You can also use TypeNameHandling = TypeNameHandling.Auto - this will add a $type property ONLY for instances where the declared type (i.e. Base) does not match the instance type (i.e. Derived). This way, it doesn't bloat your JSON as much as TypeNameHandling.All.
I keep receiving Error resolving type specified in JSON '..., ...'. Path '$type', line 1, position 82. Any ideas?
Be careful when using this on a public endpoint as it opens up security issues: alphabot.com/security/blog/2017/net/…
@gjvdkamp JEEZ thanks for this, I did not know about this. Will add to my post.
6 months later... I would strongly discourage the use of this. Aside from the security concerns, this also makes it a pain to rename your classes or move them into other namespaces. Refactoring will become hell. I now use a custom "discriminator field" on my objects to know what subclass to deserialize them into. It's the same idea basically, but it's just not tied to class namespaces...
|
37

Since the question is so popular, it may be useful to add on what to do if you want to control the type property name and its value.

The long way is to write custom JsonConverters to handle (de)serialization by manually checking and setting the type property.

A simpler way is to use JsonSubTypes, which handles all the boilerplate via attributes:

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

9 Comments

I get the need, but I'm not a fan of having to make the base class aware of all the "KnownSubType"s...
There are other options if you look at the documentation. I only provided the example I like more.
This is the safer approach that doesn't expose your service to load arbitrary types upon de-serialization.
Where is JsonSubtypes even defined? I'm using Newtonsoft.Json Version 12.0.0.0 and have no reference to JsonSubtypes, JsonSubTypes nor JsonSubtypesConverterBuilder (mentioned in that article).
@MattArnold It's a separate Nuget package.
|
9

Use this JsonKnownTypes, it's very similar way to use, it just add discriminator to json:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))] [JsonKnownType(typeof(Base), "base")] [JsonKnownType(typeof(Derived), "derived")] public class Base { public string Name; } public class Derived : Base { public string Something; } 

Now when you serialize object in json will be add "$type" with "base" and "derived" value and it will be use for deserialize

Serialized list example:

[ {"Name":"some name", "$type":"base"}, {"Name":"some name", "Something":"something", "$type":"derived"} ] 

Comments

0

In .NET 6/7 you could use System.Text.Json.Serialization

[JsonDerivedType(typeof(Panel), typeDiscriminator: "panel")] 

See https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonderivedtypeattribute.-ctor?view=net-7.0&f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(System.Text.Json.Serialization.JsonDerivedTypeAttribute.%2523ctor)%3Bk(DevLang-csharp)%26rd%3Dtrue

4 Comments

Where should this code be put? Please also add an excerpt from the link. The link can become dead in the future and your answer would remain useless.
I know this is a bit old at this point but JsonDerivedType is only available in .NET 7/8 according to that link.
Are you suggesting to use System.Text.Json implementation ? It is describe in different question is-polymorphic-deserialization-possible-in-system-text-json This question is about newtonsoft json.net
JsonDerivedType works for .NET7+ (no .NET6- implementations from MS). See "Applies to" section here

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.