Удаление десериализации полиморфных classов json без информации о типе с использованием json.net

Этот вызов Imgur api возвращает список, содержащий classы Gallery Image и Gallery Album, представленные в json.

Я не вижу, как их десериализовать автоматически, используя Json.NET, учитывая, что свойство $ type не указывает десериализатор, который должен быть представлен. Существует свойство «IsAlbum», которое можно использовать для разграничения между ними.

Этот вопрос, кажется, показывает один метод, но он выглядит немного взломанным.

Как мне заняться десериализацией этих classов? (используя C #, Json.NET) .

Пример данных:

Изображение галереи

{ "id": "OUHDm", "title": "My most recent drawing. Spent over 100 hours.", ... "is_album": false } 

Альбом галереи

 { "id": "lDRB2", "title": "Imgur Office", ... "is_album": true, "images_count": 3, "images": [ { "id": "24nLu", ... "link": "http://sofru.miximages.com/c%23/24nLu.jpg" }, { "id": "Ziz25", ... "link": "http://sofru.miximages.com/c%23/Ziz25.jpg" }, { "id": "9tzW6", ... "link": "http://sofru.miximages.com/c%23/9tzW6.jpg" } ] } 

}

Вы можете сделать это довольно легко, создав пользовательский JsonConverter для обработки экземпляра объекта. Предполагая, что у вас есть classы, что-то вроде этого:

 public abstract class GalleryItem { public string id { get; set; } public string title { get; set; } public string link { get; set; } public bool is_album { get; set; } } public class GalleryImage : GalleryItem { // ... } public class GalleryAlbum : GalleryItem { public int images_count { get; set; } public List images { get; set; } } 

Вы бы создали такой конвертер:

 public class GalleryItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(GalleryItem).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject item = JObject.Load(reader); if (item["is_album"].Value()) { return item.ToObject(); } else { return item.ToObject(); } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Вот пример программы, показывающей конвертер в действии:

 class Program { static void Main(string[] args) { string json = @" [ { ""id"": ""OUHDm"", ""title"": ""My most recent drawing. Spent over 100 hours."", ""link"": ""http://sofru.miximages.com/c%23/OUHDm.jpg"", ""is_album"": false }, { ""id"": ""lDRB2"", ""title"": ""Imgur Office"", ""link"": ""http://alanbox.imgur.com/a/lDRB2"", ""is_album"": true, ""images_count"": 3, ""images"": [ { ""id"": ""24nLu"", ""link"": ""http://sofru.miximages.com/c%23/24nLu.jpg"" }, { ""id"": ""Ziz25"", ""link"": ""http://sofru.miximages.com/c%23/Ziz25.jpg"" }, { ""id"": ""9tzW6"", ""link"": ""http://sofru.miximages.com/c%23/9tzW6.jpg"" } ] } ]"; List items = JsonConvert.DeserializeObject>(json, new GalleryItemConverter()); foreach (GalleryItem item in items) { Console.WriteLine("id: " + item.id); Console.WriteLine("title: " + item.title); Console.WriteLine("link: " + item.link); if (item.is_album) { GalleryAlbum album = (GalleryAlbum)item; Console.WriteLine("album images (" + album.images_count + "):"); foreach (GalleryImage image in album.images) { Console.WriteLine(" id: " + image.id); Console.WriteLine(" link: " + image.link); } } Console.WriteLine(); } } } 

И вот вывод вышеупомянутой программы:

 id: OUHDm title: My most recent drawing. Spent over 100 hours. link: http://i.imgur.com/OUHDm.jpg id: lDRB2 title: Imgur Office link: http://alanbox.imgur.com/a/lDRB2 album images (3): id: 24nLu link: http://i.imgur.com/24nLu.jpg id: Ziz25 link: http://i.imgur.com/Ziz25.jpg id: 9tzW6 link: http://i.imgur.com/9tzW6.jpg 

Просто с атрибутами JsonSubTypes, которые работают с Json.NET

  [JsonConverter(typeof(JsonSubtypes), "is_album")] [JsonSubtypes.KnownSubType(typeof(GalleryAlbum), true)] [JsonSubtypes.KnownSubType(typeof(GalleryImage), false)] public abstract class GalleryItem { public string id { get; set; } public string title { get; set; } public string link { get; set; } public bool is_album { get; set; } } public class GalleryImage : GalleryItem { // ... } public class GalleryAlbum : GalleryItem { public int images_count { get; set; } public List images { get; set; } } 

После выполнения следует разрешить сериализацию без изменения способа разработки ваших classов и с помощью поля, отличного от $ type, для принятия решения о его де-сериализации.

 public class GalleryImageConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(GalleryImage) || objectType == typeof(GalleryAlbum)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try { if (!CanConvert(objectType)) throw new InvalidDataException("Invalid type of object"); JObject jo = JObject.Load(reader); // following is to avoid use of magic strings var isAlbumPropertyName = ((MemberExpression)((Expression>)(s => s.is_album)).Body).Member.Name; JToken jt; if (!jo.TryGetValue(isAlbumPropertyName, StringComparison.InvariantCultureIgnoreCase, out jt)) { return jo.ToObject(); } var propValue = jt.Value(); if(propValue) { resultType = typeof(GalleryAlbum); } else{ resultType = typeof(GalleryImage); } var resultObject = Convert.ChangeType(Activator.CreateInstance(resultType), resultType); var objectProperties=resultType.GetProperties(); foreach (var objectProperty in objectProperties) { var propType = objectProperty.PropertyType; var propName = objectProperty.Name; var token = jo.GetValue(propName, StringComparison.InvariantCultureIgnoreCase); if (token != null) { objectProperty.SetValue(resultObject,token.ToObject(propType)?? objectProperty.GetValue(resultObject)); } } return resultObject; } catch (Exception ex) { throw; } } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Я только публикую это, чтобы прояснить некоторые путаницы. Если вы работаете с предопределенным форматом и нуждаетесь в десериализации, это то, что я нашел лучше всего работающим и демонстрирует механику, чтобы другие могли настраивать ее по мере необходимости.

 public class BaseClassConverter : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var j = JObject.Load(reader); var retval = BaseClass.From(j, serializer); return retval; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } public override bool CanConvert(Type objectType) { // important - do not cause subclasses to go through this converter return objectType == typeof(BaseClass); } } // important to not use attribute otherwise you'll infinite loop public abstract class BaseClass { internal static Type[] Types = new Type[] { typeof(Subclass1), typeof(Subclass2), typeof(Subclass3) }; internal static Dictionary TypesByName = Types.ToDictionary(t => t.Name.Split('.').Last()); // type property based off of class name [JsonProperty(PropertyName = "type", Required = Required.Always)] public string JsonObjectType { get { return this.GetType().Name.Split('.').Last(); } set { } } // convenience method to deserialize a JObject public static new BaseClass From(JObject obj, JsonSerializer serializer) { // this is our object type property var str = (string)obj["type"]; // we map using a dictionary, but you can do whatever you want var type = TypesByName[str]; // important to pass serializer (and its settings) along return obj.ToObject(type, serializer) as BaseClass; } // convenience method for deserialization public static BaseClass Deserialize(JsonReader reader) { JsonSerializer ser = new JsonSerializer(); // important to add converter here ser.Converters.Add(new BaseClassConverter()); return ser.Deserialize(reader); } } 
  • Почему, когда конструктор аннотируется с помощью @JsonCreator, его аргументы должны быть аннотированы с помощью @JsonProperty?
  • Json.NET: десериализация вложенных словарей
  • Java: объект для байта и байт для конвертера объектов (для Tokyo Cabinet)
  • Как изменить имена свойств при сериализации с помощью Json.net?
  • Преобразование любого объекта в массив байтов в java
  • JSON.Net выдает StackOverflowException при использовании
  • Deserialize специфическое перечисление в system.enum в Json.Net
  • Рекомендации по сериализации объектов в пользовательский строковый формат для использования в выходном файле
  • Существуют ли эквиваленты C ++ для функций протокола ввода-вывода протокола Buffer в Java?
  • Как настроить сериализацию списка объектов JAXB для JSON?
  • Пользовательский серализатор Gson для одной переменной (из многих) в объекте с использованием TypeAdapter
  • Interesting Posts

    Как вы должны диагностировать ошибку? SEHException – Внешний компонент выбрал исключение

    jQuery.post () .done () и успех:

    Почему существует разница между ping «localhost» и ping «локальный IP-адрес»?

    Изменение разрешений для файлов Windows 7 из командной строки

    Двойной монитор (IGP с графическим процессором) в Linux

    шестнадцатеричная плавающая константа в C

    Трехпроводные USB-кабели

    Могу ли я показать прогресс копирования файла с помощью FileInfo.CopyTo () в .NET?

    Как выбрать конкретный элемент формы в jQuery?

    Когда я печатаю «ничего не выходит, и если я его снова наберу, то 2 из них выйдет как таковой»,

    Сравнение двух чисел в пакетном скрипте

    Почему Java Generics не поддерживает примитивные типы?

    Как установить статический IP-адрес в окне 8.1 Host с Hyper-V и одним сетевым адаптером

    Возможные причины WINDOWS \ system32 \ drivers \ etc \ hosts не работает (appart из очевидного)

    Почему константные выражения имеют исключение для неопределенного поведения?

    Давайте будем гением компьютера.