Web Api错误地反序列化枚举列表

所以,我正在使用Web API控制器来接受JSON请求。 它映射到包含枚举列表的模型对象。 我遇到的问题是,如果JSON包含无效值,它似乎没有正确反序列化。 我希望将无效值映射到我的枚举列表中的0值类型,但这不会发生。

我已经分离出3个主要案例:如果JSON的forms是

... "MyEnumList":["IncorrectEnum", "One", "Two"] ... 

该值根本没有映射,我只是得到一个包含两个有效值的列表。 但是,如果我提供此JSON:

  ... "MyEnumList":["123", "One", "Two"] ... 

我得到一个包含3个对象的列表,其中第一个对象的类型为“MyEnum”,其值为123,即使在我的枚举中没有定义。 如果我提供此JSON语法,也会发生同样的情况:

  ... "MyEnumList":[123, "One", "Two"] ... 

任何人都可以解释这里发生了什么,以及如何确保值始终映射到有效类型?

供参考,模型对象,包含我的枚举列表:

  public class MyClass { public List MyEnumList { get; set; } } 

和简单的枚举:

  public enum myEnum { Zero = 0, One = 1, Two = 2 } 

123可以被分配给不包含123值的枚举这一事实并不完全是Json.Net的错。 事实certificate,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 。 但是,我不知道为什么你的第一个案例没有例外。 当我尝试时,它失败了(正如我所料)。

在任何情况下,如果您需要严格validation可能错误的枚举值,您可以创建一个自定义JsonConverter ,它将强制该值有效(或者可选地抛出exception)。 这是一个适用于任何枚举的转换器。 (代码可能会改进,但它有效。)

 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(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 MyEnumList { get; set; } } } 

这是输出:

 Zero One Two Zero One Two Zero One Two Zero One Two Zero Zero Zero Zero Zero Zero Zero 

顺便说一下,要将此转换器与Web API一起使用,您需要将此代码添加到Global.asax.cs Application_Start()方法:

 JsonSerializerSettings settings = GlobalConfiguration.Configuration.Formatters .JsonFormatter.SerializerSettings; settings.Converters.Add(new StrictEnumConverter());