Jon*_*ase 6 c# .net-core system.text.json
我的一些API端点的模型都包含枚举。FluentValidation用于验证发送的值是否满足各自的要求。
为了有助于可用性和文档生成,枚举被允许作为字符串而不是整数发送。如果发送的整数无效,则验证发送的值是否在正确的范围内是可行的,但是如果发送的字符串无效,则序列化将失败。
public enum Foo
{
A = 1,
B = 2
}
public class Bar
{
public Foo? Foo {get;set;}
}
void Main()
{
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var jsonString = "{\"foo\": \"C\"}";
var jsonSpan = (ReadOnlySpan<byte>)Encoding.UTF8.GetBytes(jsonString);
try
{
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
Console.WriteLine(result.Foo == null);
}
catch(Exception ex)
{
Console.WriteLine("Serialization Failed");
}
}
Run Code Online (Sandbox Code Playgroud)
我期望的结果是,当字符串与枚举的任何字段都不匹配时,将枚举属性简单地反序列化为null,以便可以将模型传递给验证器以创建友好消息。
我该如何实现?这是通过System.Text.Json API使用网络核心3预览版8。
据我所尝试,我有2种解决方案,一种使用System.Text.Json,另一种是Newtonsoft。
系统文本
您使用创建一个自定义类 JsonConverter
您在Foo中引入未知枚举。
代替使用 JsonStringEnumConverter
options.Converters.Add(new JsonStringEnumConverter());
Run Code Online (Sandbox Code Playgroud)
使用您的自定义课程 CustomEnumConverter
options.Converters.Add(new CustomEnumConverter());
Run Code Online (Sandbox Code Playgroud)
因此,让我们放在一起:
public enum Foo
{
A = 1,
B = 2,
// what ever name and enum number that fits your logic
Unknown = 99
}
public class Bar
{
public Foo? Foo { get; set; }
}
public static void Main()
{
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomEnumConverter());
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
var jsonString = "{\"foo\": \"C\"}";
var jsonSpan = (ReadOnlySpan<byte>)Encoding.UTF8.GetBytes(jsonString);
try
{
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
if (result.Foo == Foo.Unknown)
result.Foo = null;
Console.WriteLine(result.Foo == null);
}
catch (Exception ex)
{
Console.WriteLine("Serialization Failed" + ex.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
这是代码CustomEnumConverter
internal sealed class CustomEnumConverter : JsonConverter<Foo>
{
public override Foo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.String:
var isNullable = IsNullableType(typeToConvert);
var enumType = isNullable ? Nullable.GetUnderlyingType(typeToConvert) : typeToConvert;
var names = Enum.GetNames(enumType ?? throw new InvalidOperationException());
if (reader.TokenType != JsonTokenType.String) return Foo.Unknown;
var enumText = System.Text.Encoding.UTF8.GetString(reader.ValueSpan);
if (string.IsNullOrEmpty(enumText)) return Foo.Unknown;
var match = names.FirstOrDefault(e => string.Equals(e, enumText, StringComparison.OrdinalIgnoreCase));
return (Foo) (match != null ? Enum.Parse(enumType, match) : Foo.Unknown);
default:
throw new ArgumentOutOfRangeException();
}
}
public override void Write(Utf8JsonWriter writer, Foo value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
private static bool IsNullableType(Type t)
{
return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
Run Code Online (Sandbox Code Playgroud)
运行此代码应返回True且没有异常。
对于此解决方案,我从这里得到了一些启发。
另一种方法有点类似,但使用的是Newtonsoft。
注意:请记住,我在这里所做的只是演示内容的示例,请在生产之前验证所有内容并进行测试。
Newtonsoft(原始答案)
解决此问题的另一种方法是使用Newtonsoftcustom JsonConverter。
您所做的是将自定义属性添加JsonConverter到Foo类[JsonConverter(typeof(CustomEnumConverter))]。
然后,null如果enum未识别,则使您的类方法返回。
当然,您几乎可以自定义任何类型,并具有不同的自定义类。
确定通过Nuget Manager安装Newtonsoft.Json nuget软件包。
我们从修改代码开始:
//add the attribute here
[JsonConverter(typeof(CustomEnumConverter))]
public enum Foo
{
A = 1,
B = 2
}
public class Bar
{
public Foo? Foo { get; set; }
}
public static void Main()
{
var jsonString = "{\"foo\": \"C\"}";
try
{
// use newtonsoft json converter
var result = JsonConvert.DeserializeObject<Bar>(jsonString);
Console.WriteLine(result.Foo == null);
}
catch (Exception ex)
{
Console.WriteLine("Serialization Failed" + ex.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
现在是您的定制类:
public class CustomEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var type = IsNullableType(objectType) ? Nullable.GetUnderlyingType(objectType) : objectType;
return type != null && type.IsEnum;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var isNullable = IsNullableType(objectType);
var enumType = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType;
var names = Enum.GetNames(enumType ?? throw new InvalidOperationException());
if (reader.TokenType != JsonToken.String) return null;
var enumText = reader.Value.ToString();
if (string.IsNullOrEmpty(enumText)) return null;
var match = names.FirstOrDefault(e => string.Equals(e, enumText, StringComparison.OrdinalIgnoreCase));
return match != null ? Enum.Parse(enumType, match) : null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override bool CanWrite => true;
private static bool IsNullableType(Type t)
{
return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
Run Code Online (Sandbox Code Playgroud)
现在是测试时间。
当我们不执行程序而启动程序时,[JsonConverter(typeof(CustomEnumConverter))]将显示错误,如下所示:

但是,当我们添加[JsonConverter(typeof(CustomEnumConverter))]并再次运行该程序时,它可以工作:

链接:
| 归档时间: |
|
| 查看次数: |
232 次 |
| 最近记录: |