web-dev-qa-db-ja.com

Json.Netを使用したC#列挙型の逆シリアル化:値を型に変換中にエラーが発生しました

Json.NETを使用していくつかのJSONAPIをシリアル化/逆シリアル化します。

API応答には、アプリケーションで定義された列挙型にマップされるいくつかの整数値があります。

列挙型は次のようになります。

public enum MyEnum
    {
        Type1,
        Type2,
        Type3
}

jsonAPI応答には次のものがあります。

{
        "Name": "abc",
        "MyEnumValue":"Type1"
}

次のように、APIが列挙型で定義されていないMyEnumValueフィールドの値を返すことがあります。

{
            "Name": "abc",
            "MyEnumValue":"Type4"
    }

それは例外をスローします:

値 "Type4"をタイプ 'MyEnum'に変換中にエラーが発生しました

アプリケーションのクラッシュを回避するためにデフォルト値などを割り当てることで、このエラーを処理する方法はありますか?

15
Mina Samy

次のjson文字列があるとします。

[
    {
        "Name": "abc",
        "MyEnumValue": "Type1"
    },
    {
        "Name": "abcd",
        "MyEnumValue": "Type2"
    },
    {
        "Name": "abcde",
        "MyEnumValue": "Type3"
    }    ,
    {
        "Name": "abcdef",
        "MyEnumValue": "Type4"
    }
]

および次のクラスと列挙型:

public class MyClass
{
    public string Name { get; set; }

    public MyEnum MyEnumValue { get; set; }
}

public enum MyEnum
{
    Type1,
    Type2,
    Type3
}

お気づきのとおり、json文字列配列にはアイテム(最後のアイテム)が含まれていますが、これはMyEnumに正しくマップできません。デシリアライズエラーを回避するには、次のコードスニペットを使用できます。

static void Main(string[] args)
{         
    var serializationSettings = new JsonSerializerSettings
    {
        Error = HandleDeserializationError
    };

    var lst = JsonConvert.DeserializeObject<List<MyClass>>(jsonStr, serializationSettings);
}

public static void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
    errorArgs.ErrorContext.Handled = true;
    var currentObj = errorArgs.CurrentObject as MyClass;

    if (currentObj == null) return;
    currentObj.MyEnumValue = MyEnum.Type2;            
}

ここで、jsonStr変数は、上記の投稿されたjson文字列です。上記のコードサンプルでは、​​MyEnumValueを正しく解釈できない場合、デフォルト値のType2に設定されています。

例: https://dotnetfiddle.net/WKd2Lt

10
Ilija Dimov

私がそれを見る唯一の方法は、あなた自身のコンバーターを書くべきです。しかし、作業の半分はすでにクラスStringEnumConverterで行われています。オーバーライドできるのはReadJsonメソッドのみです

class Program
{
    static void Main(string[] args)
    {
        const string json = @"{
                'Name': 'abc',
                'Type':'Type4'
            }";

        // uncomment this if you want to use default value other then default enum first value
        //var settings = new JsonSerializerSettings();
        //settings.Converters.Add(new FooTypeEnumConverter { DefaultValue = FooType.Type3 });

        //var x = JsonConvert.DeserializeObject<Foo>(json, settings);

        var x = JsonConvert.DeserializeObject<Foo>(json);
    }
}

public class Foo
{
    public string Name { get; set; }

    public FooType Type { get; set; }
}

public enum FooType
{
    Type1,
    Type2,
    Type3
}

public class FooTypeEnumConverter : StringEnumConverter
{
    public FooType DefaultValue { get; set; }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch (JsonSerializationException)
        {
            return DefaultValue;
        }
    }
}
10
t3mplar

カスタムコンバーターを作成したくない場合は、それをDTOのプライベート文字列フィールドにマップしてから、そのフィールドのプロパティゲッターでEnum.TryParseを使用することもできます。

public class MyClass
{
    [JsonProperty("MyEnumValue")]
    private string myEnumValue;

    public string Name { get; set; }

    [JsonIgnore]
    public MyEnum MyEnumValue 
    { 
        get
        {
            MyEnum outputValue = MyEnum.Default;
            Enum.TryParse(myEnumValue, out outputValue);
            return outputValue;
        }
    }
}
1
darasd