Удаление десериализации полиморфных 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); } } 
  • Java: объект для байта и байт для конвертера объектов (для Tokyo Cabinet)
  • Пользовательский серализатор Gson для одной переменной (из многих) в объекте с использованием TypeAdapter
  • Как добавить тип в белый список политики сериализации GWT?
  • Как настроить сериализацию списка объектов JAXB для JSON?
  • Может ли Json.NET сериализовать / десериализовать в / из streamа?
  • Как десериализовать объект JObject для .NET
  • Как использовать пользовательский Serializer с Jackson?
  • Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
  • Серийный цикл ActiveRecord с использованием JSON вместо YAML
  • «Тип не ожидается», используя DataContractSerializer - но это просто class, не смешные вещи?
  • Добавление к ObjectOutputStream
  • Interesting Posts

    Как отключить создание .Spotlight-V100 и .Trash папок на внешнем диске?

    почему отсутствует серверный сервер, установленный в режиме онлайн / офлайн?

    Как использовать TabHost.OnTabChangeListener в android?

    Игнорировать все зависимости для конкретного пакета с помощью apt-get

    Как найти, какие обещания необработаны в Node.js UnhandledPromiseRejectionWarning?

    Как я могу обойти Windows File Protection, чтобы переименовать системный файл?

    Как сделать изображение подходящим в круговой рамке в андроиде

    Java: сравнение двойных значений

    Есть ли надстройка браузера для фильтрации результатов поиска Google?

    Реализация расстояния Левенштейна для mysql / нечеткого поиска?

    Как определить, открыт ли порт на сервере Windows?

    пользовательский информационный оконный адаптер с пользовательскими данными на карте v2

    Как показать схему таблицы в базе данных MySQL?

    Заставить приложения Windows 8 Metro работать с более низким разрешением ниже 1024×768?

    PowerQuery: как я могу объединить сгруппированные значения?

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