Тот факт, что 123
может получить назначен на перечисление, что делает не содержит значения для 123
, не является полной ошибкой Json.Net. Оказывается, само выполнение C# позволяет это назначение. Вы можете увидеть это для себя, если вы запустите эту небольшую демонстрационную программу:
class Program
{
static void Main(string[] args)
{
// Direct cast from integer -- no error here
MyEnum x = (MyEnum)123;
Console.WriteLine(x);
// Parsing a numeric string -- no error here either
MyEnum y = (MyEnum)Enum.Parse(typeof(MyEnum), "456");
Console.WriteLine(y);
}
public enum MyEnum
{
Zero = 0,
One = 1,
Two = 2
}
}
Так что, скорее всего, происходит это, что Json.Net просто использует Enum.Parse
за кулисами. Однако я не знаю, почему вы не получаете исключение в своем первом случае. Когда я пытаюсь это сделать, он терпит неудачу (как и следовало ожидать).
В любом случае, если вам требуется строгое подтверждение возможных значений перечислимого значения, вы можете создать настраиваемый JsonConverter
, который заставит это значение быть действительным (или, возможно, выдать исключение). Вот конвертер, который должен работать для любого вида перечисления. (Код, вероятно, может быть улучшен, но это работает.)
class StrictEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType.BaseType == typeof(Enum));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
try
{
// We're only interested in integers or strings;
// all other token types should fall through
if (token.Type == JTokenType.Integer ||
token.Type == JTokenType.String)
{
// Get the string representation of the token
// and check if it is numeric
string s = token.ToString();
int i;
if (int.TryParse(s, out i))
{
// If the token is numeric, try to find a matching
// name from the enum. If it works, convert it into
// the actual enum value; otherwise punt.
string name = Enum.GetName(objectType, i);
if (name != null)
return Enum.Parse(objectType, name);
}
else
{
// We've got a non-numeric value, so try to parse it
// as is (case insensitive). If this doesn't work,
// it will throw an ArgumentException.
return Enum.Parse(objectType, s, true);
}
}
}
catch (ArgumentException)
{
// Eat the exception and fall through
}
// We got a bad value, so return the first value from the enum as
// a default. Alternatively, you could throw an exception here to
// halt the deserialization.
IEnumerator en = Enum.GetValues(objectType).GetEnumerator();
en.MoveNext();
return en.Current;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
Вот демо с помощью преобразователя на солянку из значений:
class Program
{
static void Main(string[] args)
{
// The first 12 values should deserialize to correct values;
// the last 7 should all be forced to MyEnum.Zero.
string json = @"
{
""MyEnumList"":
[
""Zero"",
""One"",
""Two"",
0,
1,
2,
""zero"",
""one"",
""two"",
""0"",
""1"",
""2"",
""BAD"",
""123"",
123,
1.0,
null,
false,
true
]
}";
MyClass obj = JsonConvert.DeserializeObject<MyClass>(json,
new StrictEnumConverter());
foreach (MyEnum e in obj.MyEnumList)
{
Console.WriteLine(e.ToString());
}
}
public enum MyEnum
{
Zero = 0,
One = 1,
Two = 2
}
public class MyClass
{
public List<MyEnum> MyEnumList { get; set; }
}
}
А вот выход:
Zero
One
Two
Zero
One
Two
Zero
One
Two
Zero
One
Two
Zero
Zero
Zero
Zero
Zero
Zero
Zero
Кстати, использовать этот конвертер с Web API, вам нужно добавить этот код в ваш метод Application_Start()
в Global.asax.cs
:
JsonSerializerSettings settings = GlobalConfiguration.Configuration.Formatters
.JsonFormatter.SerializerSettings;
settings.Converters.Add(new StrictEnumConverter());
Тестирование это, если я посылаю: ... «MyEnumList»: [ «123», «Один»] ... я получаю те же результаты, как и раньше, что значение перечисления, которое равным 123. Это не сопоставление с допустимым значением перечисления. –
Вы добавили конвертер в глобальную конфигурацию, как показано выше? –
Да, хотя я считаю, что нашел проблему. Я украшал список с помощью [JsonConverter (typeof (StringEnumConverter))], который, казалось, вызывал эту проблему. –