137

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?

Madmenyo
  • 8,389
  • 7
  • 52
  • 99
Will
  • 10,013
  • 9
  • 45
  • 77
  • That isn't how the inheritance works. You can specify JsonConvert.Deserialize(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. – M.Babcock Dec 14 '11 at 23:13
  • 1
    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. – Will Dec 14 '11 at 23:18
  • I did you solve this. I have the same problem – Luis Carlos Chavarría Jul 29 '12 at 20:18

6 Answers6

125

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.

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
Madmenyo
  • 8,389
  • 7
  • 52
  • 99
  • 34
    +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. – Erti-Chris Eelmaa Jul 13 '15 at 18:53
  • 31
    `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. – Roman Starkov Feb 16 '16 at 10:16
  • 5
    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'. – Jebathon May 22 '20 at 17:06
  • 2
    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! – Noob001 Oct 27 '21 at 16:15
  • @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 – Michael Freidgeim Apr 20 '23 at 08:06
58

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.

kamranicus
  • 4,207
  • 2
  • 39
  • 57
  • 29
    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`. – AJ Richardson Mar 25 '15 at 13:18
  • I keep receiving Error resolving type specified in JSON '..., ...'. Path '$type', line 1, position 82. Any ideas? – briba Oct 21 '16 at 23:09
  • 5
    Be careful when using this on a public endpoint as it opens up security issues: https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-vulnerable-web-API.html – gjvdkamp Aug 13 '18 at 10:59
  • 2
    @gjvdkamp JEEZ thanks for this, I did not know about this. Will add to my post. – kamranicus Oct 02 '18 at 03:02
  • For those searching, this is how to do the same thing with `System.Text.Json.Serialization`: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-polymorphism – ryanwebjackson Jul 20 '22 at 18:53
  • 1
    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... – Eyad Arafat Aug 24 '22 at 19:57
36

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; }
}
rzippo
  • 979
  • 12
  • 22
  • 8
    I get the need, but I'm not a fan of having to make the base class aware of all the "KnownSubType"s... – Matt Knowles Apr 04 '19 at 23:18
  • 2
    There are other options if you look at the documentation. I only provided the example I like more. – rzippo Apr 05 '19 at 09:58
  • 2
    This is the safer approach that doesn't expose your service to load arbitrary types upon de-serialization. – David Burg Dec 19 '19 at 23:40
  • 1
    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). – Matt Arnold Nov 16 '20 at 18:37
  • 1
    @MattArnold It's a separate Nuget package. – rzippo Nov 16 '20 at 19:33
  • I don't understand the need of the second parameter - is it not just possible to do something equivalent to `[JsonSubtypes.KnownSubType(typeof(Cat))]` to tell the deserializer to use `Cat` if it finds a `Declawed` value? – Sphynx Nov 17 '20 at 01:59
  • I can't get this to work! I've marked the base class alike above only with `null` for the second parameter for `KnownSubType` because I have nothing to give it; I've also tried `KnownSubTypeWithPropertyAttribute(typeof(Cat), nameof(Cat.Declawed))` and even added `jsonSerializer.Converters.Add(JsonSubtypesConverterBuilder.Of(nameof(Cat.Declawed)).RegisterSubtype(AnimalType.Cat).SerializeDiscriminatorProperty().Build());` to both the serializer and deserializer but only the base class properties are deserialized even though the sub class properties are present in the JSON! – Matt Arnold Nov 17 '20 at 15:36
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"}
]
Dmitry
  • 192
  • 1
  • 3
0

In .NET 6/7 you could use

[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

Misho73bg
  • 38
  • 2
  • 4
  • 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. – sanitizedUser Jul 08 '23 at 06:48
-1

just add object in Serialize method

 var jsonMessageBody = JsonSerializer.Serialize<object>(model);
xxxsenatorxxx
  • 121
  • 2
  • 13