-1

I am looking to create a custom JSON converter on my C# application. When I instantiate a new class I am unable to leave it generic.

Class:

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

public class Converter<T> : JsonConverter<T> where T : Enum
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string? data = reader.GetString();
        return data.GetEnum<T>()!;
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        writer.WriteString(value.GetDescription<T>());
    }
}
  • Can't you use the built in enum converter for this? https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter – itsdaniel0 Feb 20 '23 at 17:45
  • You see the custom converter is needed as I need to call GetEnumValue and/or GetDescription to perform other actions. While this works as intended when I specify the actual enum, I am unable to run it by mentioning a generic type. – marcusredbie Feb 20 '23 at 17:47
  • 1
    Still you may want to take a look at the implementation of that Converter so you know where it leads to. Especially the use of the EnumConverterFactory. https://source.dot.net/#System.Text.Json/System/Text/Json/Serialization/JsonStringEnumConverter.cs,66907b2ded1a2bb8 – Ralf Feb 20 '23 at 17:51
  • I've taken a look at that link and a custom converter is needed in this scenario. – marcusredbie Feb 20 '23 at 18:15
  • 1
    You should use the [factory pattern](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-6-0#sample-factory-pattern-converter) to manufacture specific converters for each Enum type as you encounter it. See [this answer](https://stackoverflow.com/a/59061296/3744182) to [System.Text.Json: How do I specify a custom name for an enum value?](https://stackoverflow.com/q/59059989/3744182) for an example. – dbc Feb 20 '23 at 18:26
  • Thanks DBC. All enums should get treated the same under 'read' and 'write'. But I am unable to let the enum be declared as generic. – marcusredbie Feb 20 '23 at 18:34

2 Answers2

1

You should use the factory pattern to manufacture specific converters for each Enum type as you encounter it:

public class CustomEnumConverter : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum; // Add anything additional here such as typeToConvert.IsEnumWithDescription() to check for description attributes.

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
        (JsonConverter)Activator.CreateInstance(typeof(CustomConverter<>).MakeGenericType(typeToConvert))!;

    class CustomConverter<T> : JsonConverter<T> where T : struct, Enum
    {
        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
            reader.GetString()!.GetEnumValue<T>()!;

        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) =>
            writer.WriteStringValue(value.GetDescription<T>());
    }
}

A factory converter can be applied directly to properties as well as long as the factory can return a converter for the property's type, so there is no need to make the inner converter public:

public class Model
{
    [JsonConverter(typeof(CustomEnumConverter))]
    public SomeEnum? SomeEnum { get; set; }
}

In fact, the built-in JsonStringEnumConverter is also implemented via the factory pattern.

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340
-1

You should check the already existing JsonStringEnumConverter: https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter

x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());

Hylaean
  • 1,237
  • 13
  • 19