Use a Blanket policy to serialize enums as strings with snake case

118 views Asked by At

I'm moving some libraries to .net 8 and I'm trying to use the new JsonNamingPolicy.SnakeCaseLower for enums (I have a custom converter that I currently use that uses reflection but I want to drop it). I can serialize an enum to snake case using this JsonSerializerOptions

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
};

options.Converters.Add(new JsonStringEnumConverter(namingPolicy: JsonNamingPolicy.SnakeCaseLower));

The problem is that according to the documentation The non-generic JsonStringEnumConverter type is not supported for AOT. The solution given is using [JsonSourceGenerationOptions(UseStringEnumConverter = true)] in the class inheriting from JsonSerializerContext but then I lose the naming policy for enums.

Is there a way to use JsonNamingPolicy.SnakeCaseLower globally for all enums in an AOT friendly way?

1

There are 1 answers

0
dbc On BEST ANSWER

This is not implemented as of .NET 8, see [API Proposal]: Add option to specify JsonNamingPolicy for enum serialization on JsonSourceGenerationOptions #92828.

Recently there has been added UseStringEnumConverter flag to JsonSourceGenerationOptions, but there is no way to configure naming policy for these converters. -ithline.

Sounds reasonable. -eiriktsarpalis.

MSFT's Eirik Tsarpalis suggests, as a workaround, to define a JsonStringEnumConverter<TEnum> subtype for your required naming policy and, for each enum, add it to your serialization context, e.g. like so:

public class SnakeCaseLowerStringEnumConverter<TEnum>() 
    : JsonStringEnumConverter<TEnum>(JsonNamingPolicy.SnakeCaseLower) 
    where TEnum : struct, Enum;

[JsonSourceGenerationOptions(
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
    Converters = new[] { 
        // Add all known enums here
        typeof(SnakeCaseLowerStringEnumConverter<FirstEnum>), 
        typeof(SnakeCaseLowerStringEnumConverter<SecondEnum>),
        //...
        typeof(SnakeCaseLowerStringEnumConverter<LastEnum>)})]
// Add all known enums here also
[JsonSerializable(typeof(FirstEnum)), 
 JsonSerializable(typeof(SecondEnum)),
 //...
 JsonSerializable(typeof(LastEnum))]
// And also add all other root types to be serialized in this context
[JsonSerializable(typeof(MyModel)),] 
public partial class SnakeCaseLowerContext : JsonSerializerContext { }

This, however, requires knowing all the enum types in advance. There's no way to set a blanket policy.