使用在启动时全局设置的 JsonStringEnumConverter 排除模型的枚举属性?

Sat*_*uru 6 c# json .net-core asp.net-core system.text.json

我正在使用最新的 .NET Core 3.1.1 开发 ASP.NET Core 应用程序,而System.Text.Json该应用程序之前使用的是Newtonsoft.Json. 按照 Microsoft迁移指南中的建议 ,我已经完成了更改。另外,由于我的大多数枚举需要序列化为字符串,因此我已将 Startup.cs 配置ConfigureServices为全局使用JsonStringEnumConverter

public void ConfigureServices(IServiceCollection services)
{
    // lines omitted for brevity

    services.AddControllers()
                .AddJsonOptions(options =>
                    {
                        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
                        options.JsonSerializerOptions.IgnoreNullValues = true;
                        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
                    });
}
Run Code Online (Sandbox Code Playgroud)

但最近,在发布之后,我们意识到只有少数枚举通过我们的 API 以 json 形式给出。由于这些 API 是外部使用的,因此将数字更改为字符串可能是一件代价高昂的事情。

那么,有没有办法忽略某些枚举属性的通用性,例如带有属性的装饰[JsonIgnore]

dbc*_*dbc 13

JsonStringEnumConverter实际上是 的子类JsonConverterFactory。它为序列化过程中遇到的JsonConverterEnum每个具体enum类型创建一个特定类型,然后将该特定enum类型序列化为字符串。

如果您不希望某些特定enum 类型被序列化为字符串,则可以使用装饰器模式并创建自己的转换器工厂来装饰 aJsonStringEnumConverter但阻止该enum类型被转换,如下所示:

public class OptOutJsonConverterFactory : JsonConverterFactoryDecorator
{
    readonly HashSet<Type> optOutTypes;

    public OptOutJsonConverterFactory(JsonConverterFactory innerFactory, params Type [] optOutTypes) : base(innerFactory) => this.optOutTypes = optOutTypes.ToHashSet();

    public override bool CanConvert(Type typeToConvert) => base.CanConvert(typeToConvert) && !optOutTypes.Contains(typeToConvert);
}

public class JsonConverterFactoryDecorator : JsonConverterFactory
{
    readonly JsonConverterFactory innerFactory;

    public JsonConverterFactoryDecorator(JsonConverterFactory innerFactory)
    {
        if (innerFactory == null)
            throw new ArgumentNullException(nameof(innerFactory));
        this.innerFactory = innerFactory;
    }

    public override bool CanConvert(Type typeToConvert) => innerFactory.CanConvert(typeToConvert);

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => innerFactory.CreateConverter(typeToConvert, options);
}
Run Code Online (Sandbox Code Playgroud)

然后在选项中使用它,如下所示:

options.Converters.Add(new OptOutJsonConverterFactory(new JsonStringEnumConverter(), 
                                                      // Add here all enum types to serialize as integers:
                                                      typeof(SomeEnumNotToSerializeAsAString)
                                                      //, ...
                                                     ));
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 如果维护要序列化为整数的枚举类型列表不方便,您可以使用某些自定义属性标记要序列化为整数的枚举类型,然后从内部排除标有该属性的类型CanConvert(CanConvert(Type typeToConvert)

  • 装饰器模式是必需的,因为JsonStringEnumConverter它是密封的。

模型小提琴#1在这里

或者,如果您不希望将某些特定enum 属性序列化为字符串,则可以使用转换器将转换器应用于该属性,JsonConverterAttribute以忽略传入JsonSerializerOptions并生成默认序列化:

/// <summary>
/// Apply this converter to a property to force the property to be serialized with default options.  
/// This converter can ONLY be applied to a property; setting it in options or on a type may cause a stack overflow exception!
/// </summary>
/// <typeparam name="T">the property's declared return type</typeparam>
public class SerializePropertyAsDefaultConverter<T> : JsonConverter<T>
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return JsonSerializer.Deserialize<T>(ref reader); // Ignore the incoming options!
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value); // Ignore the incoming options!
    } 
}
Run Code Online (Sandbox Code Playgroud)

并将其应用到您的模型中,如下所示:

public class Model
{
    public StringEnum StringEnum { get; set; }

    [JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
    public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 该解决方案利用了转换器的记录优先级

    1. [JsonConverter]应用于财产。
    2. 添加到 Converters 集合中的转换器。
    3. [JsonConverter]应用于自定义值类型或 POCO。

模型小提琴#2在这里