Deserializing JSON, когда иногда массивы, а иногда и объекты
У меня возникли проблемы с десериализацией данных, полученных из Facebook с использованием библиотек JSON.NET.
JSON вернулся с простого настенного столба:
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
Возврат JSON для фотографии выглядит следующим образом:
"attachment":{
"media":[
{
"href":"http://www.facebook.com/photo.php?fbid=12345",
"alt":"",
"type":"photo",
"src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg",
"photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}}
],
Все отлично работает, и у меня нет проблем. Теперь я столкнулся с простым стеновым сообщением от мобильного клиента со следующим JSON, и теперь десериализация не выполняется с этим единственным сообщением:
"attachment":
{
"media":{},
"name":"",
"caption":"",
"description":"",
"properties":{},
"icon":"http://www.facebook.com/images/icons/mobile_app.gif",
"fb_object_type":""
},
"permalink":"http://www.facebook.com/1234"
Вот класс, который я десериализую как:
public class FacebookAttachment
{
public string Name { get; set; }
public string Description { get; set; }
public string Href { get; set; }
public FacebookPostType Fb_Object_Type { get; set; }
public string Fb_Object_Id { get; set; }
[JsonConverter(typeof(FacebookMediaJsonConverter))]
public List<FacebookMedia> { get; set; }
public string Permalink { get; set; }
}
Без использования FacebookMediaJsonConverter я получаю сообщение об ошибке: нельзя десериализовать объект JSON в тип "System.Collections.Generic.List`1 [FacebookMedia]".
что имеет смысл, поскольку в JSON Media не является коллекцией.
Я нашел этот пост, который описывает аналогичную проблему, поэтому я попытался спуститься по этому маршруту: Deserialize JSON, иногда значение представляет собой массив, иногда "" (пустая строка)
Мой конвертер выглядит так:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
return serializer.Deserialize<List<FacebookMedia>>(reader);
else
return null;
}
Что работает отлично, но теперь я получаю новое исключение:
Внутри JsonSerializerInternalReader.cs, CreateValueInternal(): Неожиданный токен при десериализации объекта: PropertyName
Значение reader.Value является "permalink". Я могу ясно видеть в коммутаторе, что нет никакого случая для JsonToken.PropertyName.
Есть ли что-то, что мне нужно сделать по-другому в моем конвертере? Спасибо за любую помощь.
Ответы
Ответ 1
Разработчик JSON.NET оказывал помощь на сайте codeplex проектов. Вот решение:
Проблема заключалась в том, что когда это был объект JSON, я не читал атрибут. Вот правильный код:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<FacebookMedia>>(reader);
}
else
{
FacebookMedia media = serializer.Deserialize<FacebookMedia>(reader);
return new List<FacebookMedia>(new[] {media});
}
}
Джеймс также был достаточно любезен, чтобы предоставить единичные тесты для вышеупомянутого метода.
Ответ 2
Очень подробное объяснение того, как обращаться с этим случаем, доступно в "Использование пользовательского JsonConverter для исправления плохих результатов JSON" .
Подводя итог, вы можете расширить конвертер JSON.NET по умолчанию, делая
-
Аннотировать свойство с проблемой
[JsonConverter(typeof(SingleValueArrayConverter<OrderItem>))]
public List<OrderItem> items;
-
Расширьте конвертер, чтобы вернуть список желаемого типа даже для одного объекта
public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
} else if (reader.TokenType == JsonToken.StartArray) {
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
Как уже упоминалось в статье, это расширение не является полностью общим, но оно работает, если вы в порядке со списком.
Ответ 3
взгляните на пространство имен System.Runtime.Serialization в фреймворке С#, и вы получите то, где вы хотите быть очень быстрым.
Если вы хотите, вы можете проверить код примера в этот проект (не пытаясь подключить мою собственную работу, но я только что закончил в значительной степени именно то, что вы делаете, но с другим источником api.
надеюсь, что это поможет.
Ответ 4
Я думаю, вы должны написать свой класс, как это...!!!
public class FacebookAttachment
{
[JsonProperty("attachment")]
public Attachment Attachment { get; set; }
[JsonProperty("permalink")]
public string Permalink { get; set; }
}
public class Attachment
{
[JsonProperty("media")]
public Media Media { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("properties")]
public Properties Properties { get; set; }
[JsonProperty("icon")]
public string Icon { get; set; }
[JsonProperty("fb_object_type")]
public string FbObjectType { get; set; }
}
public class Media
{
}
public class Properties
{
}