簡體   English   中英

通過字符串轉換反序列化枚舉時,如何獲取空值而不是序列化錯誤?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM