dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.27k stars 4.73k forks source link

[API Proposal]: Add option to specify JsonNamingPolicy for enum serialization on JsonSourceGenerationOptions #92828

Open ithline opened 1 year ago

ithline commented 1 year ago

Background and motivation

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

The Converters property accepts Type[]? array, so it cannot be used either.

API Proposal


namespace System.Text.Json.Serialization;

public partial class JsonSourceGenerationOptionsAttribute : JsonAttribute
{
    public JsonKnownNamingPolicy EnumNamingPolicy { get; set; }
}

API Usage

[JsonSourceGenerationOptions(
    UseStringEnumConverter = true,
    EnumNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
internal sealed partial class MyJsonContext : JsonSerializerContext
{
}

Alternative Designs

No response

Risks

No response

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation Recently there has been added `UseStringEnumConverter` flag to `JsonSourceGenerationOptions`, but there is no way to configure naming policy for these converters. The `Converters` property accepts `Type[]?` array, so it cannot be used either. ### API Proposal ```csharp namespace System.Text.Json.Serialization; public partial class JsonSourceGenerationOptionsAttribute : JsonAttribute { public JsonKnownNamingPolicy EnumNamingPolicy { get; set; } } ``` ### API Usage ```csharp [JsonSourceGenerationOptions( UseStringEnumConverter = true, EnumNamingPolicy = JsonKnownNamingPolicy.CamelCase)] internal sealed partial class MyJsonContext : JsonSerializerContext { } ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: ithline
Assignees: -
Labels: `api-suggestion`, `area-System.Text.Json`
Milestone: -
eiriktsarpalis commented 1 year ago

Sounds reasonable.

eiriktsarpalis commented 1 year ago

The Converters property accepts Type[]? array, so it cannot be used either.

You can always try extending the converter class with a custom naming policy and specify that in the Converters property:

public enum MyEnum { A, B, C };

[JsonSourceGenerationOptions(Converters = new[] { typeof(CamelCaseJsonStringEnumConverter<MyEnum>)})]
[JsonSerializable(typeof(MyEnum))]
public partial class MyContext : JsonSerializerContext { }

public class CamelCaseJsonStringEnumConverter<TEnum>() 
    : JsonStringEnumConverter<TEnum>(JsonNamingPolicy.CamelCase) where TEnum : struct, Enum;
alexanderdibenedetto commented 10 months ago

This would solve a lot of issues with the fact that the JsonStringEnumConverter locks you into CamelCase, and some of us need snake or kebob case instead for certain enumerations.

Pentadome commented 5 months ago

The Converters property accepts Type[]? array, so it cannot be used either.

You can always try extending the converter class with a custom naming policy and specify that in the Converters property:

public enum MyEnum { A, B, C };

[JsonSourceGenerationOptions(Converters = new[] { typeof(CamelCaseJsonStringEnumConverter<MyEnum>)})]
[JsonSerializable(typeof(MyEnum))]
public partial class MyContext : JsonSerializerContext { }

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

Or you can make a factory to support all enums.

internal sealed class JsonStringEnumCamelCaseConverter : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert.IsEnum;
    }

    public override JsonConverter? CreateConverter(
        Type typeToConvert,
        JsonSerializerOptions options
    )
    {
        if (!typeToConvert.IsEnum)
            throw new ArgumentException("Type should be enum.", nameof(typeToConvert));

        var constructedType = typeof(JsonStringEnumConverter<>).MakeGenericType(typeToConvert);
        return (JsonConverter)
            Activator.CreateInstance(
                constructedType,
                BindingFlags.Default,
                null,
                [JsonNamingPolicy.CamelCase, true],
                null
            )!;
    }
}