对具有不同结构和相同名称的json对象进行反序列化



我写了一个应用程序,通过抓取电影页面源来获取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对象。有一个名为"Director"的属性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; }
}

另一个问题是如何反序列化由Person类和Organization类生成的创建者属性。

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

谢谢

您尝试过吗: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();
}
}

[更新]

至于sometime single,sometime array的问题-->请看这里:如何使用JSON.net 处理同一属性的单个项和数组

谢谢@Piotr。它完全奏效了。因为你的第一部分答案对我来说不正确,我把你的回答改写为答案。

正如你所说,正确答案在这个链接中。

https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n所以我上了这门课。

class JsonConverter<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; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("image")]
public string Image { get; set; }
[JsonProperty("genre")]
[JsonConverter(typeof(JsonConverter<string>))]
public List<string> Genre { get; set; }
[JsonProperty("contentRating")]
public string ContentRating { get; set; }
[JsonProperty("actor")]
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Actor { get; set; }
[JsonProperty("director")] 
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Director { get; set; }
[JsonProperty("creator")]
[JsonConverter(typeof(JsonConverter<ImdbJsonTypeEnum>))]
public List<ImdbJsonTypeEnum> Creator { get; set; }
}

和这个Enum

public class ImdbJsonTypeEnum
{
[JsonProperty("@type")]
public TypeEnum Type { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
public enum TypeEnum
{
Organization,
Person
};
}

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

谢谢

最新更新