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

Posted

技术标签:

【中文标题】使用在启动时全局设置的 JsonStringEnumConverter 排除模型的枚举属性?【英文标题】:Exclude an enum property of a Model from using the JsonStringEnumConverter which is globally set at the Startup? 【发布时间】:2020-05-06 19:06:27 【问题描述】:

我正在使用最新的 .NET Core 3.1.1 和 System.Text.Json 开发 ASP.NET Core 应用程序,之前使用的是 Newtonsoft.Json。正如 Microsoft Migration guide 中所建议的那样 我已经完成了更改。此外,由于我的大多数枚举都需要序列化为字符串,因此我已将 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;
                    );

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

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

【问题讨论】:

【参考方案1】:

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

如果您不想将某些特定的 enum type 序列化为字符串,则可以使用 decorator pattern 并创建自己的转换器工厂来装饰 JsonStringEnumConverter 但防止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);

然后在options中使用如下:

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

注意事项:

1234563 .

需要装饰器模式,因为JsonStringEnumConverter 是密封的。

样机小提琴 #1 here.

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

/// <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!
     

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

public class Model

    public StringEnum StringEnum  get; set; 

    [JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
    public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString  get; set; 

注意事项:

此解决方案利用documented precedence for converters:

    [JsonConverter] 应用于属性。 添加到 Converters 集合中的转换器。 [JsonConverter] 应用于自定义值类型或 POCO。

样机小提琴 #2 here.

【讨论】:

感谢@dbc 的详尽解释。

以上是关于使用在启动时全局设置的 JsonStringEnumConverter 排除模型的枚举属性?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中实现全局应用程序设置

Dubbo的优化 --- 开发时使用

函数(命名空间和作用域)

IDEA全局配置

在Postman中如何设置全局变量?

如何在 emacs 启动时默认启用非全局次要模式?