2014-01-22 2 views
4

Итак, я использую API-интерфейс веб-сервера для приема запросов JSON. Это сопоставление с объектом модели, который включает список перечислений. Проблема, с которой я столкнулась, заключается в том, что если JSON содержит недопустимое значение, он, похоже, не десериализуется правильно. Я ожидал бы, что недопустимое значение будет сопоставлено с типом значения 0 в моем списке перечислений, однако этого не происходит.Web Api неправильно десериализует список перечислений

Есть 3 основных случая, которые я изолировал: Если JSON в виде

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

Значения не отображенные на всех, и я просто получить список с двумя допустимыми значениями. Однако, если я поставить этот JSON:

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

получить список с 3-х объектов, где первый объект имеет тип «MyEnum» и имеет значение 123, несмотря на то, что не определено в моем перечислении. То же самое происходит, если я поставить этот JSON синтаксис:

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

Может кто-нибудь объяснить, что здесь происходит, и как я могу гарантировать, что значения всегда отображаются на правильный тип?

Для справки, модель объекта, который содержит список моего перечисления:

public class MyClass 
    { 
     public List<myEnum> MyEnumList { get; set; } 
    } 

и простое перечисление:

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

ответ

6

Тот факт, что 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()); 
+0

Тестирование это, если я посылаю: ... «MyEnumList»: [ «123», «Один»] ... я получаю те же результаты, как и раньше, что значение перечисления, которое равным 123. Это не сопоставление с допустимым значением перечисления. –

+0

Вы добавили конвертер в глобальную конфигурацию, как показано выше? –

+0

Да, хотя я считаю, что нашел проблему. Я украшал список с помощью [JsonConverter (typeof (StringEnumConverter))], который, казалось, вызывал эту проблему. –

Смежные вопросы