使用在启动时全局设置的 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 被序列化为字符串,您可以使用忽略传入的JsonSerializerOptions
的JsonConverterAttribute
将转换器应用于属性并改为生成默认序列化:
/// <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 排除模型的枚举属性?的主要内容,如果未能解决你的问题,请参考以下文章