Cra*_*ham 22 .net c# .net-core .net-core-3.0 system.text.json
使用.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
}
Run Code Online (Sandbox Code Playgroud)
dbc*_*dbc 25
目前在.net-core-3.0中不支持开箱即用。当前有一个开放问题Support for EnumMemberAttribute in JsonConverterEnum #31081请求此功能。在此期间,您将需要创建自己的JsonConverterFactory序列化具有由属性指定的自定义值名称的枚举。
如果您需要使用自定义值名称往返枚举,则需要从头开始创建通用转换器 + 转换器工厂。这在一定程度上参与一般,因为它是必要的处理解析整数和字符串值的,重命名的各成分的[Flags]枚举值,以及所有可能的枚举底层类型(byte,short,int,long,ulong等等)。
JsonStringEnumMemberConverterMacross.Json.Extensions当使用[EnumMember(Value = "custom name")]属性修饰枚举时,from似乎提供了此功能;安装软件包Macross.Json.Extensions,然后执行:
[JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumMemberConverter))] // This custom converter was placed in a system namespace.
public enum Example
{
Trick,
Treat,
[EnumMember(Value = "Trick-Or-Treat")]
TrickOrTreat,
}
Run Code Online (Sandbox Code Playgroud)
有关使用详细信息,请参阅此处的文档。
或者,您可以使用 Json.NETStringEnumConverter作为参考模型来推出自己的模型。
如果您只需要使用自定义值名称序列化一个枚举,这可以通过创建一个JsonConverterFactory适应JsonStringEnumConverter的更轻松地完成,该适应通过JsonNamingPolicy为每个enum类型构建一个定制的,该类型查找[EnumMember(Value = "xxx")]枚举成员上是否存在属性,如果找到,则映射成员名称到属性值。(我选择EnumMember是因为这是 Newtonsoft 支持的属性。)
首先介绍如下转换器:
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 ?? throw new ArgumentNullException();
public override string ConvertName (string name) => dictionary.TryGetValue(name, out var value) ? value : base.ConvertName(name);
}
Run Code Online (Sandbox Code Playgroud)
然后装饰你的enum:
public enum Example
{
Trick,
Treat,
[EnumMember(Value = "Trick-Or-Treat")]
TrickOrTreat,
}
Run Code Online (Sandbox Code Playgroud)
并按如下方式独立使用转换器:
var options = new JsonSerializerOptions
{
Converters = { new CustomJsonStringEnumConverter() },
WriteIndented = true,
};
var json = JsonSerializer.Serialize(values, options);
Run Code Online (Sandbox Code Playgroud)
要注册使用asp.net核心转换器,例如见这个答案来JsonConverter相当于使用System.Text.Json由摩尼Gandham。
笔记:
这种方法只适用于序列化,因为JsonConverterFactory在反序列化过程中忽略了它的命名策略;请参阅System.Text.Json:JsonStringEnumConverter 在反序列化期间忽略其 JsonNamingPolicy。#31619详情。
在 .Net Core 3.x 中,转换器可能无法按需要使用[Flags]枚举,例如:
[Flags]
public enum Example
{
Trick = (1<<0),
Treat = (1<<1),
[EnumMember(Value = "Trick-Or-Treat")]
TrickOrTreat = (1<<2),
}
Run Code Online (Sandbox Code Playgroud)
像这样的简单值Example.TrickOrTreat被正确重命名,但像这样的复合值Example.Trick | Example.TrickOrTreat则没有。后者的结果应该是"Trick, Trick-Or-Treat"but is "Trick, TrickOrTreat"。
问题的原因是JsonConverterEnum<T>每个特定枚举类型的底层使用构造的复合名称T调用ConvertName一次,而不是使用复合名称的每个组件多次调用。如果需要解决方法,DictionaryLookupNamingPolicy.ConvertName()您可以尝试将传入名称拆分为逗号分隔的组件,重新映射每个组件,然后重新组合结果。
为了比较,Json.NET对复合标志值的每个组件StringEnumConverter调用等效方法NamingStrategy.ResolvePropertyName(string name),这似乎更正确。
在 .Net 5 中,这是固定的,有关详细信息,请参阅问题 #31622。
演示小提琴在这里。
| 归档时间: |
|
| 查看次数: |
18370 次 |
| 最近记录: |