Search code examples
c#genericsserializationsystem.text.json

Serialize a list of generic class objects using System.Text.Json not serializing generic properties correctly


I have an application that composes algorithms. Each algorithm consists of a sequence of steps. Steps have a very similar structure, only difference being a single property that can be one of many enum types.

There are dozens of algorithms with hundreds of steps each. I do not want to use polymorphism as this would make the code base huge.

When using generics, the code below compiles, but produces the following output:

{"Steps":[{"Type":"Type1","Value":{}},{"Type":"Type2","Value":{}}]}

Instead of:

{"Steps":[{"Type":"Type1","Value":"Bla"},{"Type":"Type2","Value":"Etc"}]}

How can I set the System.Text.Json serializer so that it serializes the enums?

using System.Text.Json;
using System.Text.Json.Serialization;

Algorithm algorithm1 = new Algorithm();
algorithm1.Steps = new List<Step<Enum>>()
{
    new Step<Enum>() { Type = AlgorithmType.Type1, Value = Options1.Bla },
    new Step<Enum>() { Type = AlgorithmType.Type2, Value = Options2.Etc }
};
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());

var result = JsonSerializer.Serialize(algorithm1, options);
Console.WriteLine(result);


public class Algorithm
{
    public List<Step<Enum>> Steps { get; set; }
}

public class Step<T> where T : Enum
{
    public AlgorithmType Type { get; set; }
    public T? Value { get; set; }
}

public enum AlgorithmType
{
    Undefined = 0,
    Type1 = 1,
    Type2 = 2,
    // elided
    Type100 = 100
}

public enum Options1
{
    Undefined = 0,
    Bla = 1,
    YadaYada = 2
}

public enum Options2
{
    Undefined = 0,
    Etc = 1,
    Eg = 2
}

Solution

  • It can be done with a custom converter:

    public class CustomEnumConverter : JsonConverter<Enum> {
        public override Enum Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
            => throw new NotSupportedException();
    
        public override void Write(
            Utf8JsonWriter writer,
            Enum value,
            JsonSerializerOptions options)
            => writer.WriteStringValue(value.ToString());
    }
    

    Usage:

    var steps = new List<Step<Enum>> {
        new Step<Enum>(Foo.Bar),
        new Step<Enum>(Zan.Gor)
    };
    
    var opts = new JsonSerializerOptions {
        Converters = { new CustomEnumConverter() }
    };
    
    var json = JsonSerializer.Serialize(steps, opts);
    
    public record Step<T>(T Value) where T : Enum;
    public enum Foo { Bar, Qux }
    public enum Zan { Qel, Gor }
    

    Result:

    [{"Value":"Bar"},{"Value":"Gor"}]
    

    I doubt that this is the right approach though. How would you go about deserializing these enums?