2015-04-04 2 views
2

Я работаю с API VK. Иногда сервер может возвращать пустой массив вместо объекта, например:Deserialize JSON, когда тип может быть другим

personal: [] //when it is empty 

или

personal: { 
religion: 'Нет', 
smoking: 1, 
alcohol: 4 
} //when not empty. 

I`m десериализации большинство с JsonConvert.DeserializeObject JSON, и эту часть с

JSON
MainObject = ((MainObject["response"].GetObject())["user"].GetObject())["personal"].GetObject(); 
try 
{ 
Convert.ToByte(MainObject["political"].GetNumber(); 
} 
catch {} 

Но он медленно работает, когда он обрабатывает множество исключений. И только сейчас я понял, что здесь есть еще несколько полей, которые могут возвращать массив при пустом. У меня просто нет идей, как сделать это быстро и ясно. Какие-либо предложения?

Мой класс десериализации (doen`t работы, когда поле пусто):

 public class User 
      { 
//some other fields... 
       public Personal personal { get; set; } 
//some other fields... 
      } 
    public class Personal 
      { 
       public byte political { get; set; } 
       public string[] langs { get; set; } 
       public string religion { get; set; } 
       public string inspired_by { get; set; } 
       public byte people_main { get; set; } 
       public byte life_main { get; set; } 
       public byte smoking { get; set; } 
       public byte alcohol { get; set; } 
      } 

Другая идея (Безразлично `работа, когда не пусто):

public List<Personal> personal { get; set; } 
+1

Попробуйте адаптировать SingleOrArrayConverter отсюда: https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using -json-n – dbc

+0

Спасибо за хорошую идею, это то, что я искал, надеюсь, что смогу это понять ... –

ответ

1

Вы можете сделать JsonConverter следующим образом, которое ищет объект определенного типа или пустой массив. Если объект, он десериализует этот объект. Если пустой массив, она возвращает нуль:

public class JsonSingleOrEmptyArrayConverter<T> : JsonConverter where T : class 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(T).IsAssignableFrom(objectType); 
    } 

    public override bool CanWrite { get { return false; } } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (existingValue == null) 
     { 
      var contract = serializer.ContractResolver.ResolveContract(objectType); 
      existingValue = contract.DefaultCreator(); 
     } 

     switch (reader.TokenType) 
     { 
      case JsonToken.StartArray: 
       { 
        var jArray = JArray.Load(reader); 
        if (jArray == null) 
         return null; 
        switch (jArray.Count) 
        { 
         case 0: 
          existingValue = null; 
          break; 

         case 1: 
          using (var sr = new StringReader(jArray[0].ToString())) 
          { 
           serializer.Populate(sr, existingValue); 
          } 
          break; 

         default: 
          throw new InvalidOperationException("Too many objects"); 
        } 
       } 
       break; 

      case JsonToken.StartObject: 
       serializer.Populate(reader, existingValue); 
       break; 

      default: 
       throw new InvalidOperationException("Unexpected token type " + reader.TokenType.ToString()); 
     } 
     return existingValue; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Затем используйте это нравится:

public class User 
{ 
    //some other fields... 
    [JsonConverter(typeof(JsonSingleOrEmptyArrayConverter<Personal>))] 
    public Personal personal { get; set; } 
    //some other fields... 
} 

Теперь вы должны быть в состоянии десериализации пользователя в ваш User класса.

Примечание. Это не предназначено для работы с простыми типами, такими как строки, оно предназначено для классов, которые отображаются на объект JSON.

+0

Отличная идея! Большое спасибо! –

+0

CS1061 \t «Тип» не содержит определения для «IsAssignableFrom», и метод расширения «IsAssignableFrom», допускающий первый аргумент типа «Тип», не может быть найден (вам не хватает директивы использования или ссылки на сборку?) Здесь : public override bool CanConvert (Тип objectType) { { return typeof (T) .IsAssignableFrom (objectType); } –

+0

return (objectType == typeof (T)); - Это сработает? –

0

Вместо использования попробовать улов переключение между двумя возможностями, просто проверьте первый символ. Если это «[', это значение null, если оно« {», то вы deserialize.

EDIT:

Теперь, учитывая, что объект не весь JSON, это дает мне идею: У нас была аналогичная проблема с API возвращения несогласованные JSON сериализации. В итоге мы использовали библиотеку ServiceStack.Text NewtonSoft (доступную от NuGet). Мы сериализуем объекты JToken вместо целевого класса. Затем мы обработали структуры JToken для частичной десериализации.

+0

Интересная идея, но проблема в том, что мне нужно делать это в любой ситуации, и у меня есть много различных json-файлов. Было бы лучше понять, что в самом классе. Кроме того, это только небольшая часть json, и я не уверен, что могу проверить ее быстро ... –

+0

Понимаю, у меня создалось впечатление, что это был весь файл JSON –

+0

Любые другие идеи? Я искал что-то вроде пользовательского десериализатора ... –

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