Search code examples
c#serializationpolymorphismsystem.text.json.net-7.0

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.


Solution

  • 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.