使用json.net反序列化没有types信息的多态json类

这Imgur api调用返回一个列表,其中包含以json表示的图库图像图库相册类。

我不能看到如何反序列化这些自动使用Json.NET,因为没有$ type属性告诉反序列化器哪个类是要表示。 有一个名为“IsAlbum”的属性可以用来区分两者。

这个问题似乎显示了一种方法,但它看起来像一个黑客。

我如何去反序列化这些类? (使用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://i.imgur.com/24nLu.jpg" }, { "id": "Ziz25", ... "link": "http://i.imgur.com/Ziz25.jpg" }, { "id": "9tzW6", ... "link": "http://i.imgur.com/9tzW6.jpg" } ] } 

}

您可以通过创build一个自定义的JsonConverter来处理对象实例化来相当容易地做到这一点。 假设你有你的类定义了这样的东西:

 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<GalleryImage> images { get; set; } } 

你可以像这样创build转换器:

 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<bool>()) { return item.ToObject<GalleryAlbum>(); } else { return item.ToObject<GalleryImage>(); } } 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://i.imgur.com/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://i.imgur.com/24nLu.jpg"" }, { ""id"": ""Ziz25"", ""link"": ""http://i.imgur.com/Ziz25.jpg"" }, { ""id"": ""9tzW6"", ""link"": ""http://i.imgur.com/9tzW6.jpg"" } ] } ]"; List<GalleryItem> items = JsonConvert.DeserializeObject<List<GalleryItem>>(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 

我只是发布这个来澄清一些混乱。 如果您正在使用预定义的格式并需要反序列化,那么这是我发现的最好的方法,并且演示了机制,以便其他人可以根据需要对其进行调整。

 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<string, Type> 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<BaseClass>(reader); } }