使用.NET Core中的 System.Text.Json 序列化程序功能,如何为枚举值指定自定义值,类似于JsonPropertyName
?例如:
public enum Example {
Trick,
Treat,
[JsonPropertyName("Trick-Or-Treat")] // Error: Attribute 'JsonPropertyName' is not valid on this declaration type. It is only valid on 'property, indexer' declarations.
TrickOrTreat
}
答案 0 :(得分:3)
当前.net-core-3.0中的属性不支持此功能。当前有一个公开问题 Support for EnumMemberAttribute in JsonConverterEnum #41578 要求该功能。
在此期间,您可以为每个enum
类型构造一个自定义的JsonConverterFactory
,以寻找{{3}的存在,从而创建自己的JsonStringEnumConverter
以适应JsonNamingPolicy
}}枚举成员的属性,如果找到,则将成员的名称映射到该属性的值。
首先,介绍以下转换器:
public class CustomJsonStringEnumConverter : JsonConverterFactory
{
private readonly JsonNamingPolicy namingPolicy;
private readonly bool allowIntegerValues;
private readonly JsonStringEnumConverter baseConverter;
public CustomJsonStringEnumConverter() : this(null, true) { }
public CustomJsonStringEnumConverter(JsonNamingPolicy namingPolicy = null, bool allowIntegerValues = true)
{
this.namingPolicy = namingPolicy;
this.allowIntegerValues = allowIntegerValues;
this.baseConverter = new JsonStringEnumConverter(namingPolicy, allowIntegerValues);
}
public override bool CanConvert(Type typeToConvert) => baseConverter.CanConvert(typeToConvert);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var query = from field in typeToConvert.GetFields(BindingFlags.Public | BindingFlags.Static)
let attr = field.GetCustomAttribute<EnumMemberAttribute>()
where attr != null
select (field.Name, attr.Value);
var dictionary = query.ToDictionary(p => p.Item1, p => p.Item2);
if (dictionary.Count > 0)
{
return new JsonStringEnumConverter(new DictionaryLookupNamingPolicy(dictionary, namingPolicy), allowIntegerValues).CreateConverter(typeToConvert, options);
}
else
{
return baseConverter.CreateConverter(typeToConvert, options);
}
}
}
public class JsonNamingPolicyDecorator : JsonNamingPolicy
{
readonly JsonNamingPolicy underlyingNamingPolicy;
public JsonNamingPolicyDecorator(JsonNamingPolicy underlyingNamingPolicy) => this.underlyingNamingPolicy = underlyingNamingPolicy;
public override string ConvertName (string name) => underlyingNamingPolicy == null ? name : underlyingNamingPolicy.ConvertName(name);
}
internal class DictionaryLookupNamingPolicy : JsonNamingPolicyDecorator
{
readonly Dictionary<string, string> dictionary;
public DictionaryLookupNamingPolicy(Dictionary<string, string> dictionary, JsonNamingPolicy underlyingNamingPolicy) : base(underlyingNamingPolicy) => this.dictionary = dictionary;
public override string ConvertName (string name)
{
if (!dictionary.TryGetValue(name, out var value))
value = base.ConvertName(name);
return value;
}
}
然后装饰您的enum
:
public enum Example
{
Trick,
Treat,
[EnumMember(Value = "Trick-Or-Treat")]
TrickOrTreat,
}
并独立使用转换器,如下所示:
var options = new JsonSerializerOptions
{
Converters = { new CustomJsonStringEnumConverter() },
WriteIndented = true,
};
var json = JsonSerializer.Serialize(values, options);
要将转换器注册到asp.net内核,请参见例如由[EnumMember(Value = "xxx")]
到this answer到 JsonConverter equivalent in using System.Text.Json 。
注意:
对于[Flags]
枚举,转换器可能无法正常工作,例如:
[Flags]
public enum Example
{
Trick = (1<<0),
Treat = (1<<1),
[EnumMember(Value = "Trick-Or-Treat")]
TrickOrTreat = (1<<2),
}
像Example.TrickOrTreat
这样的简单值已正确重命名,而像Example.Trick | Example.TrickOrTreat
这样的复合值则没有正确重命名。后者的结果应为"Trick, Trick-Or-Treat"
,但应为"Trick, TrickOrTreat"
。
问题的原因是,每种特定枚举类型T
的基础Mani Gandham用构造的复合名称调用JsonConverterEnum<T>
一次,而不用复合名称的每个组成部分多次调用ConvertName
。如果需要解决方法,则可以在DictionaryLookupNamingPolicy.ConvertName()
中尝试将传入名称拆分为以逗号分隔的组件,重新映射每个组件,然后重新组合结果。
为了进行比较,Json.NET的StringEnumConverter
在复合标志值的每个组件上调用等效方法NamingStrategy.ResolvePropertyName(string name)
,这似乎更正确。
演示小提琴here。
答案 1 :(得分:0)
对于.NET 5:
services.AddControllers()
.AddJsonOptions(opts => opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
答案 2 :(得分:-2)
在 .net-core-5.0 和 asp.net-core-5.0 中,Microsoft 添加了对通过 JsonStringEnumConverter Class 反序列化枚举的支持。
像这样装饰你的枚举值:
using System.Runtime.Serialization;
public enum VipStatus
{
[EnumMember(Value = @"IS_VIP")]
VIP = 1,
[EnumMember(Value = @"IS_NOT_VIP")]
NonVIP = 2,
}
给定一个这样的类:
class MyClass {
public VipStatus MyVipStatus { get; set; }
}
您可以使用 JsonStringEnumConverter 内联来序列化类的实例,如下所示:
using System.Text.Json;
using System.Text.Json.Serialization;
// ...
var myObjectWithEnums = new MyClass()
{
MyVipStatus = VipStatus.NonVIP
};
var options = new JsonSerializerOptions();
// Configures serialization to allow strings to be accepted and auto-converted to enum values.
options.Converters.Add(new JsonStringEnumConverter());
var json = JsonSerializer.Serialize(myObjectWithEnums, options);
// serialized output is: { "myVipStatus": "IS_NOT_VIP"}
如果您使用的是 ASP.NET Core 5,那么您可以在启动时配置应用程序以使用 JsonStringEnumConverter 来序列化所有传入请求:
public async void ConfigureServices(IServiceCollection services) {
// ...
services
.AddControllers()
.AddJsonOptions(options => {
// Configures serialization to allow strings to be accepted and auto-converted to enum values.
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
}
// ...
});
更多阅读:How to serialize and deserialize (marshal and unmarshal) JSON in .NET Core。如果您在 ASP.NET 中工作,那么这也很有趣:Web defaults for JsonSerializerOptions。