问题描述
我正在使用最新的.NET Core 3.1.1和 System.Text.Json
(以前使用的是 Newtonsoft.Json
)开发ASP.NET Core应用程序.如Microsoft 迁移指南我已完成更改.另外,由于我的大多数枚举都需要序列化为String,因此我已将Startup.cs ConfigureServices
配置为全局使用 JsonStringEnumConverter
.
I am developing the ASP.NET Core Application using the latest .NET Core 3.1.1 and System.Text.Json
which was previously using the Newtonsoft.Json
. As recommended in the Microsoft Migration guideI have done the changes. Also, as most of my enums need to be serialized as String I have configured my Startup.cs ConfigureServices
to use the JsonStringEnumConverter
globally.
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是在外部使用的,因此将数字更改为字符串可能是一项昂贵的工作.
But recently, after the release we realized that only a few enums are given out as numerals in json through our API. As these APIs are consumed externally, changing the numerals to strings might be a costly stuff.
那么,有没有办法忽略某些枚举属性的通用性,例如使用 [JsonIgnore]
属性进行修饰?
So, is there a way to ignore the universal for some enum properties like the decoration with the [JsonIgnore]
attribute?
推荐答案
JsonStringEnumConverter
实际上是 JsonConverterFactory
.它制造一个特定的 JsonConverterEnum
,用于序列化过程中遇到的每种具体的 enum
类型,进而依次序列化该特定的 enum
>输入为字符串.
JsonStringEnumConverter
is actually a subclass of JsonConverterFactory
. It manufactures a specific JsonConverterEnum
for each concrete enum
type encountered during serialization that, in turn, serializes that specific enum
type as a string.
如果您不想将某些特定的 enum
type 序列化为字符串,则可以使用装饰器模式,并创建您自己的转换器工厂,该工厂装饰 JsonStringEnumConverter
,但阻止该 enum
类型转换如下:
If you don't want some specific enum
type to be serialized as a string, you can use the decorator pattern and create your own converter factory that decorates a JsonStringEnumConverter
but prevents that enum
type from being converted as follows:
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);
}
然后在以下选项中使用它:
Then use it in options as follows:
options.Converters.Add(new OptOutJsonConverterFactory(new JsonStringEnumConverter(),
// Add here all enum types to serialize as integers:
typeof(SomeEnumNotToSerializeAsAString)
//, ...
));
注意:
-
如果维护枚举类型列表以序列化为整数不方便,则可以使用一些自定义属性,然后从
CanConvert(CanConvert(CanConvert(Type typeToConvert)
)内排除带有该属性标记的类型.
If maintaining a list of enum types to serialize as integers is inconvenient, you could mark the enum types to be serialized as integers with some custom attribute, then exclude types marked with that attribute from within
CanConvert(CanConvert(Type typeToConvert)
.
装饰器模式是必需的,因为 JsonStringEnumConverter
是密封的.
The decorator pattern is required because JsonStringEnumConverter
is sealed.
模型小提琴#1 此处.
或者,如果您不希望将某些特定的 enum
属性序列化为字符串,则可以使用 JsonConverterAttribute
会忽略传入的 JsonSerializerOptions
并生成默认的序列化:
Alternatively, if you don't want some specific enum
property to be serialized as a string, you can apply a converter to the property using JsonConverterAttribute
that ignores the incoming JsonSerializerOptions
and generates a default serialization instead:
/// <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!
}
}
并将其应用于您的模型,如下所示:
And apply it to your model as follows:
public class Model
{
public StringEnum StringEnum { get; set; }
[JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString { get; set; }
}
注意:
-
此解决方案利用了已记录的转换器优先级:
-
[JsonConverter]
应用于属性. - 已将一个转换器添加到Converters集合中.
-
[JsonConverter]
应用于自定义值类型或POCO.
[JsonConverter]
applied to a property.- A converter added to the Converters collection.
[JsonConverter]
applied to a custom value type or POCO.
模型小提琴#2 此处.
这篇关于从启动时全局设置的JsonStringEnumConverter中排除模型的枚举属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!