我写了一个应用,通过抓取电影页面源获取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对象?"
谢谢您
你有没有试过。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处理同一属性的单项和数组的问题
谢谢你@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; }
}
它适用于一个导演和多导演的电影。
谢谢你了