Десериализовать объект json с другой структурой и тем же именем - PullRequest
0 голосов
/ 06 мая 2020

Я пишу приложение, которое получает информацию IMDb mov ie, очищая исходный код страницы mov ie. Некоторые из данных mov ie в источнике страницы имеют формат JSON со схемой mov ie из "Schema.org".

{
  "@context": "http://schema.org",
  "@type": "Movie",
  "url": "/title/tt7131622/",
  "name": "Once Upon a Time... in Hollywood",
  "genre": [
    "Comedy",
    "Drama"
  ],
  "actor": [
    {
      "@type": "Person",
      "url": "/name/nm0000138/",
      "name": "Leonardo DiCaprio"
    },
    {
      "@type": "Person",
      "url": "/name/nm0000093/",
      "name": "Brad Pitt"
    },
    {
      "@type": "Person",
      "url": "/name/nm3053338/",
      "name": "Margot Robbie"
    },
    {
      "@type": "Person",
      "url": "/name/nm0386472/",
      "name": "Emile Hirsch"
    }
  ],
  "director": {
    "@type": "Person",
    "url": "/name/nm0000233/",
    "name": "Quentin Tarantino"
  },
  "creator": [
    {
      "@type": "Person",
      "url": "/name/nm0000233/",
      "name": "Quentin Tarantino"
    },
    {
      "@type": "Organization",
      "url": "/company/co0050868/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0452101/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0159772/"
    }
}

Я создал класс "Mov ie" для десериализовать объект JSON. Есть свойство Person класс с именем «Директор».

internal class ImdbJsonMovie
    {
        public string Url { get; set; }
        public string Name { get; set; }
        public string Image { get; set; }
        public List<string> Genre { get; set; }
        public List<ImdbJsonPerson> Actor { get; set; }
        public ImdbJsonPerson Director { get; set; }
        //public string[] Creator { get; set; }
    }

Все нормально. Но проблема в том, что в некоторых фильмах, таких как «Матрица», более одного режиссера.

{
  "@context": "http://schema.org",
  "@type": "Movie",
  "url": "/title/tt0133093/",
  "name": "The Matrix",
  "genre": [
    "Action",
    "Sci-Fi"
  ],
  "actor": [
    {
      "@type": "Person",
      "url": "/name/nm0000206/",
      "name": "Keanu Reeves"
    },
    {
      "@type": "Person",
      "url": "/name/nm0000401/",
      "name": "Laurence Fishburne"
    },
    {
      "@type": "Person",
      "url": "/name/nm0005251/",
      "name": "Carrie-Anne Moss"
    },
    {
      "@type": "Person",
      "url": "/name/nm0915989/",
      "name": "Hugo Weaving"
    }
  ],
  "director": [
    {
      "@type": "Person",
      "url": "/name/nm0905154/",
      "name": "Lana Wachowski"
    },
    {
      "@type": "Person",
      "url": "/name/nm0905152/",
      "name": "Lilly Wachowski"
    }
  ],
  "creator": [
    {
      "@type": "Person",
      "url": "/name/nm0905152/",
      "name": "Lilly Wachowski"
    },
    {
      "@type": "Person",
      "url": "/name/nm0905154/",
      "name": "Lana Wachowski"
    },
    {
      "@type": "Organization",
      "url": "/company/co0002663/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0108864/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0060075/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0019968/"
    },
    {
      "@type": "Organization",
      "url": "/company/co0070636/"
    }
}

Значит, это должно быть List<Person>.

internal class ImdbJsonMovie
    {
        public string Url { get; set; }
        public string Name { get; set; }
        public string Image { get; set; }
        public List<string> Genre { get; set; }
        public List<ImdbJsonPerson> Actor { get; set; }
        public List<ImdbJsonPerson> Director { get; set; }
        //public string[] Creator { get; set; }
    }

Другая проблема - как десериализовать Creator, которое создается классом Person и Organization.

Итак, вопрос: «Как десериализовать этот сложный JSON объект?»

Спасибо

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Вы пробовали: https://app.quicktype.io/?l=csharp? Он может сгенерировать для вас модель в C#, что является очень хорошим началом для дальнейших изменений (если схема должна отличаться в соответствии с разными json ответами)

Я ввел ваш JSON и Созданная модель выглядит следующим образом:

namespace QuickType
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Movies
    {
        [JsonProperty("@context")]
        public Uri Context { get; set; }

        [JsonProperty("@type")]
        public string Type { get; set; }

        [JsonProperty("url")]
        public string Url { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("genre")]
        public List<string> Genre { get; set; }

        [JsonProperty("actor")]
        public List<Tor> Actor { get; set; }

        [JsonProperty("director")]
        public List<Tor> Director { get; set; }

        [JsonProperty("creator")]
        public List<Tor> Creator { get; set; }
    }

    public partial class Tor
    {
        [JsonProperty("@type")]
        public TypeEnum Type { get; set; }

        [JsonProperty("url")]
        public string Url { get; set; }

        [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
        public string Name { get; set; }
    }

    public enum TypeEnum { Organization, Person };

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                TypeEnumConverter.Singleton,
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }

    internal class TypeEnumConverter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(TypeEnum) || t == typeof(TypeEnum?);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            var value = serializer.Deserialize<string>(reader);
            switch (value)
            {
                case "Organization":
                    return TypeEnum.Organization;
                case "Person":
                    return TypeEnum.Person;
            }
            throw new Exception("Cannot unmarshal type TypeEnum");
        }

        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
        {
            if (untypedValue == null)
            {
                serializer.Serialize(writer, null);
                return;
            }
            var value = (TypeEnum)untypedValue;
            switch (value)
            {
                case TypeEnum.Organization:
                    serializer.Serialize(writer, "Organization");
                    return;
                case TypeEnum.Person:
                    serializer.Serialize(writer, "Person");
                    return;
            }
            throw new Exception("Cannot marshal type TypeEnum");
        }

        public static readonly TypeEnumConverter Singleton = new TypeEnumConverter();
    }
}

[Обновление]

Что касается проблем с когда-то одиночным, иногда с массивом -> смотрите здесь: Как сделать обрабатывать как отдельный элемент, так и массив для одного и того же свойства, используя JSON. net

0 голосов
/ 06 мая 2020

Спасибо @Piotr. Это полностью сработало. поскольку ваша первая часть ответа была неправильной для меня, я перепишу ваш ответ как ответ.

как вы сказали, правильный ответ был в этой ссылке. Как обрабатывать как отдельный элемент, так и массив для одного и того же свойства, используя JSON. net

Итак, я создал этот класс.

class SingleOrArrayJsonConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(List<T>);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            if (token.Type == JTokenType.Array)
            {
                return token.ToObject<List<T>>();
            }
            return new List<T> { token.ToObject<T>() };
        }

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

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

и изменил мой класс Mov ie на этот.

internal class ImdbJsonMovie
{
    public string Url { get; set; }
    public string Name { get; set; }
    public string Image { get; set; }
    public List<string> Genre { get; set; }
    public List<ImdbJsonPerson> Actor { get; set; }

    [JsonProperty("director")] 
    [JsonConverter(typeof(SingleOrArrayJsonConverter<ImdbJsonPerson>))]
    public List<ImdbJsonPerson> Director { get; set; }

    //public string[] Creator { get; set; }
}

Это сработало для фильмов с одним и несколькими режиссерами.

Спасибо

...