С# Enum deserialization с Json.Net: Ошибка преобразования значения в тип

Я использую Json.NET для сериализации/десериализации некоторых API-интерфейсов JSON.

Ответ API имеет некоторые целочисленные значения, которые сопоставляются с Enum, определенным в приложении.

Перечисление выглядит следующим образом:

public enum MyEnum
    {
        Type1,
        Type2,
        Type3
}

и ответ json API имеет следующее:

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

иногда API возвращает значение для поля MyEnumValue, которое не определено в моем перечислении, например:

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

Это генерирует исключение:

Ошибка преобразования значения "Тип4" для ввода "MyEnum"

Есть ли способ обработать эту ошибку, назначив значение по умолчанию или что-то, чтобы избежать сбоя приложения?

Ответ 1

Скажем, у нас есть следующая строка 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 string содержит элемент (последний), который не может быть правильно отображен на 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

Ответ 2

Единственный способ, которым я это вижу, - написать собственный конвертер. Но половина работы уже выполнена в классе 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;
        }
    }
}

Ответ 3

Альтернативой, если вы не хотите создавать собственный конвертер, является сопоставление его с частным строковым полем вашего DTO, а затем используйте Enum.TryParse в этом полевом свойстве getter:

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;
        }
    }
}