[英]System.Text.Json: when deserializing, how can I ignore a null or empty string value for a property that has a default value set in the constructor?
[英]How can I get a null value instead of a serialization error when deserializing an enum by string conversion?
我的一些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");
}
}
我期望的結果是,當字符串與枚舉的任何字段都不匹配時,將枚舉屬性簡單地反序列化為null,以便可以將模型傳遞給驗證器以創建友好消息。
我該如何實現? 這是通過System.Text.Json API使用網絡核心3預覽版8。
據我所嘗試,我有2種解決方案,一種使用System.Text.Json
,另一種是Newtonsoft
。
系統文本
您使用JsonConverter
創建一個自定義類
您在Foo中引入未知枚舉。
代替使用JsonStringEnumConverter
options.Converters.Add(new JsonStringEnumConverter());
使用您的自定義類CustomEnumConverter
options.Converters.Add(new CustomEnumConverter());
因此,讓我們放在一起:
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);
}
}
這是代碼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<>));
}
}
運行此代碼應返回True且沒有異常。
對於此解決方案,我從這里得到了一些啟發。
另一種方法有點類似,但使用的是Newtonsoft。
注意:請記住,我在這里所做的只是演示內容的示例,請在生產之前驗證所有內容並進行測試。
Newtonsoft(原始答案)
使用帶有自定義JsonConverter
Newtonsoft
解決此問題的另一種方法。
您所做的是將自定義JsonConverter
屬性添加到Foo類[JsonConverter(typeof(CustomEnumConverter))]
。
然后,如果無法識別enum
則使您的類方法返回null
。
當然,您幾乎可以自定義任何類型,並具有不同的自定義類。
確定通過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);
}
}
現在是您的定制類:
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<>));
}
}
現在是測試時間。
當我們在不使用[JsonConverter(typeof(CustomEnumConverter))]
的情況下啟動程序時,將出現錯誤,如下所示:
但是,當我們添加[JsonConverter(typeof(CustomEnumConverter))]
並再次運行該程序時,它可以工作:
鏈接:
您可以反序列化為字符串和TryParse
public class Bar
{
public string Foo { get; set; }
public Foo? FooEnum { get; set; }
}
...
var result = JsonSerializer.Deserialize<Bar>(jsonSpan, options);
Enum.TryParse<Foo>(result, out Bar.FooEnum);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.