Tono Nam
Tono Nam

Reputation: 36050

Using JsonDerivedType attribute to serialize and deserialize Polymorphic objects in .NET 7

JSON.NET (by Newtonsoft) has great support for serializing and deserializing complex objects.

I am curious about using System.Text.Json instead of JSON.NET and I am not able to find a good tutorial on the internet.

The .NET 7 preview has support for deserializing polymorphic objects. Here is an example using .NET 7 preview and C# 11:

// _To run it you will need net7 preview and c# 11_

using System.Text.Json.Serialization;

var testPolymorphism = new TestPolymorphysm()
{
    Animals = new List<Animal>()
    {
        new Fish() {
             Id = "fish1",
             Name = "GoldFish",
             Action = new ActionSwim() { DistanceSwam = 10 }
        },
        new Dog() {
             Id = "dog1",
             Name = "Tom",
             Action = new ActionRun() { DistanceRan = 50  }
        }
    }
};

// serialize
var jsonSerialized = System.Text.Json.JsonSerializer.Serialize(testPolymorphism);
Console.WriteLine(jsonSerialized);

// deserialize
var clonePolymorphysm = System.Text.Json.JsonSerializer.Deserialize<TestPolymorphysm>(jsonSerialized);
Console.WriteLine(clonePolymorphysm);


// === MODELS ===

class TestPolymorphysm
{
    public List<Animal> Animals { get; set; } = new();
}

[JsonDerivedType(derivedType: typeof(Dog), typeDiscriminator: "foo1")]
[JsonDerivedType(derivedType: typeof(Fish), typeDiscriminator: "foo2")]
abstract class Animal
{
    public required string Id { get; set; }
    public required string Name { get; set; }
}

class Dog : Animal
{
    public required IAction Action { get; set; }
    public AnimalType ExtensionType => AnimalType.Dog;
}

class Fish : Animal
{
    public required IAction Action { get; set; }
    public AnimalType ExtensionType => AnimalType.Fish;
}

[JsonDerivedType(derivedType: typeof(ActionSwim), typeDiscriminator: "foo3")]
[JsonDerivedType(derivedType: typeof(ActionRun), typeDiscriminator: "foo4")]
interface IAction { }
class ActionSwim : IAction
{
    public required int DistanceSwam { get; set; }
}

class ActionRun : IAction
{
    public required int DistanceRan { get; set; }
}

public enum AnimalType
{
    Fish,
    Dog
}

Anyways this code works thanks to JsonDerivedType attributes but I am not sure why it works. Why is it that if I remove the typeDiscriminators foo1, foo2, foo3 and foo4 it does not work? I want to make sure I understand how it works before I use it.

Upvotes: 5

Views: 10162

Answers (2)

patyx
patyx

Reputation: 339

The process is described in the documentation: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0#polymorphic-type-discriminators

To enable polymorphic deserialization, you must specify a type discriminator for the derived class ... With the added metadata, specifically, the type discriminator, the serializer can serialize and deserialize the payload ... Serialization will emit JSON along with the type discriminator metadata

Essentially, the JsonDerivedTypeAttribute identifies supported derived types and adds the type discriminator $type as JSON metadata, which in turn instructs the serializer.

Upvotes: 3

Tono Nam
Tono Nam

Reputation: 36050

Sorry I was not paying attention to the serialized object. It contains: "$type": "foo1",, "$type": "foo2", etc..

That's why the deserializer knows how to deserialize the object.

Upvotes: 2

Related Questions