Ответ 1
Вы можете решить эту проблему с помощью настраиваемого JsonConverter
. Вот один из них, который я собрал, используя несколько частей из класса StringEnumConverter
, который исходит от Json.Net. Это должно дать вам гибкость в том, чтобы обращаться с вещами независимо от того, как вы решаете. Вот как это работает:
- Если значение, найденное в JSON, соответствует перечислению (либо как строка, либо целое число), это значение используется. (Если значение целое и существует несколько возможных совпадений, используется первый из них.)
- В противном случае, если тип перечисления является нулевым, значение равно null.
- В противном случае, если перечисление имеет значение, называемое "Неизвестно", это значение используется.
- В противном случае используется первое значение перечисления.
Если вам не нравится способ работы правил или его упрощение, не стесняйтесь. Здесь код:
class TolerantEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
Type type = IsNullableType(objectType) ? Nullable.GetUnderlyingType(objectType) : objectType;
return type.IsEnum;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
bool isNullable = IsNullableType(objectType);
Type enumType = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType;
string[] names = Enum.GetNames(enumType);
if (reader.TokenType == JsonToken.String)
{
string enumText = reader.Value.ToString();
if (!string.IsNullOrEmpty(enumText))
{
string match = names
.Where(n => string.Equals(n, enumText, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault();
if (match != null)
{
return Enum.Parse(enumType, match);
}
}
}
else if (reader.TokenType == JsonToken.Integer)
{
int enumVal = Convert.ToInt32(reader.Value);
int[] values = (int[])Enum.GetValues(enumType);
if (values.Contains(enumVal))
{
return Enum.Parse(enumType, enumVal.ToString());
}
}
if (!isNullable)
{
string defaultName = names
.Where(n => string.Equals(n, "Unknown", StringComparison.OrdinalIgnoreCase))
.FirstOrDefault();
if (defaultName == null)
{
defaultName = names.First();
}
return Enum.Parse(enumType, defaultName);
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
private bool IsNullableType(Type t)
{
return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
Вот демоверсия, которая ставит его конвертер через его шаги, используя пару разных перечислений (у одного есть значение "Неизвестно", а другое нет):
[JsonConverter(typeof(TolerantEnumConverter))]
enum Status
{
Ready = 1,
Set = 2,
Go = 3
}
[JsonConverter(typeof(TolerantEnumConverter))]
enum Color
{
Red = 1,
Yellow = 2,
Green = 3,
Unknown = 99
}
class Foo
{
public Status NonNullableStatusWithValidStringValue { get; set; }
public Status NonNullableStatusWithValidIntValue { get; set; }
public Status NonNullableStatusWithInvalidStringValue { get; set; }
public Status NonNullableStatusWithInvalidIntValue { get; set; }
public Status NonNullableStatusWithNullValue { get; set; }
public Status? NullableStatusWithValidStringValue { get; set; }
public Status? NullableStatusWithValidIntValue { get; set; }
public Status? NullableStatusWithInvalidStringValue { get; set; }
public Status? NullableStatusWithInvalidIntValue { get; set; }
public Status? NullableStatusWithNullValue { get; set; }
public Color NonNullableColorWithValidStringValue { get; set; }
public Color NonNullableColorWithValidIntValue { get; set; }
public Color NonNullableColorWithInvalidStringValue { get; set; }
public Color NonNullableColorWithInvalidIntValue { get; set; }
public Color NonNullableColorWithNullValue { get; set; }
public Color? NullableColorWithValidStringValue { get; set; }
public Color? NullableColorWithValidIntValue { get; set; }
public Color? NullableColorWithInvalidStringValue { get; set; }
public Color? NullableColorWithInvalidIntValue { get; set; }
public Color? NullableColorWithNullValue { get; set; }
}
class Program
{
static void Main(string[] args)
{
string json = @"
{
""NonNullableStatusWithValidStringValue"" : ""Set"",
""NonNullableStatusWithValidIntValue"" : 2,
""NonNullableStatusWithInvalidStringValue"" : ""Blah"",
""NonNullableStatusWithInvalidIntValue"" : 9,
""NonNullableStatusWithNullValue"" : null,
""NullableStatusWithValidStringValue"" : ""Go"",
""NullableStatusWithValidIntValue"" : 3,
""NullableStatusWithNullValue"" : null,
""NullableStatusWithInvalidStringValue"" : ""Blah"",
""NullableStatusWithInvalidIntValue"" : 9,
""NonNullableColorWithValidStringValue"" : ""Green"",
""NonNullableColorWithValidIntValue"" : 3,
""NonNullableColorWithInvalidStringValue"" : ""Blah"",
""NonNullableColorWithInvalidIntValue"" : 0,
""NonNullableColorWithNullValue"" : null,
""NullableColorWithValidStringValue"" : ""Yellow"",
""NullableColorWithValidIntValue"" : 2,
""NullableColorWithNullValue"" : null,
""NullableColorWithInvalidStringValue"" : ""Blah"",
""NullableColorWithInvalidIntValue"" : 0,
}";
Foo foo = JsonConvert.DeserializeObject<Foo>(json);
foreach (PropertyInfo prop in typeof(Foo).GetProperties())
{
object val = prop.GetValue(foo, null);
Console.WriteLine(prop.Name + ": " +
(val == null ? "(null)" : val.ToString()));
}
}
}
Вывод:
NonNullableStatusWithValidStringValue: Set
NonNullableStatusWithValidIntValue: Set
NonNullableStatusWithInvalidStringValue: Ready
NonNullableStatusWithInvalidIntValue: Ready
NonNullableStatusWithNullValue: Ready
NullableStatusWithValidStringValue: Go
NullableStatusWithValidIntValue: Go
NullableStatusWithInvalidStringValue: (null)
NullableStatusWithInvalidIntValue: (null)
NullableStatusWithNullValue: (null)
NonNullableColorWithValidStringValue: Green
NonNullableColorWithValidIntValue: Green
NonNullableColorWithInvalidStringValue: Unknown
NonNullableColorWithInvalidIntValue: Unknown
NonNullableColorWithNullValue: Unknown
NullableColorWithValidStringValue: Yellow
NullableColorWithValidIntValue: Yellow
NullableColorWithInvalidStringValue: (null)
NullableColorWithInvalidIntValue: (null)
NullableColorWithNullValue: (null)