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