反序列化一个具有不同结构和相同名称的json对象。

问题描述 投票:0回答:1

我写了一个应用,通过抓取电影页面源获取IMDb电影信息。页面源中的一些电影数据是JSON格式的,电影模式来自 "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/"
    }
}

我做了一个 "Movie "类来反序列化JSON对象。有一个属性 Person 命名为 "Director "的类。

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; }
    }

另一个问题是如何反序列化创造者属性,是由 Person 阶级和 Organization 类。

所以问题是 "如何反序列化这个复杂的JSON对象?"

谢谢您

c# json deserialization imdb
1个回答
1
投票

你有没有试过。https:/app.quicktype.io?l=csharp。? 它可以在C#中为你生成模型,这对于进一步的修改是一个很好的开端(如果Schema必须根据不同的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
投票

谢谢你@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();
        }
    }

并把我的电影课改成了这个。

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; }
}

它适用于一个导演和多导演的电影。

谢谢你了

© www.soinside.com 2019 - 2024. All rights reserved.