此Imgur api来电会返回包含以JSON格式表示的图库图片和图库相册类的列表。
我无法看到如何使用Json.NET自动反序列化这些,因为没有$ type属性告诉反序列化器要表示哪个类。有一个名为“IsAlbum”的属性可用于区分两者。
This问题似乎显示了一种方法,但它看起来有点像黑客。
如何对这些类进行反序列化? (使用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"
}
]
}
}
答案 0 :(得分:89)
通过创建自定义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; }
}
您可以像这样创建转换器:
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
答案 1 :(得分:18)
只需使用适用于Json.NET的JsonSubTypes属性
[JsonConverter(typeof(JsonSubtypes), "is_album")]
[JsonSubtypes.KnownSubType(typeof(GalleryAlbum), true)]
[JsonSubtypes.KnownSubType(typeof(GalleryImage), false)]
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; }
}
答案 2 :(得分:1)
实现之后,您应该在不改变设计类的方式的情况下反序列化,并使用$ type以外的字段来决定将其反序列化的内容。
public class GalleryImageConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(GalleryImage) || objectType == typeof(GalleryAlbum));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
if (!CanConvert(objectType))
throw new InvalidDataException("Invalid type of object");
JObject jo = JObject.Load(reader);
// following is to avoid use of magic strings
var isAlbumPropertyName = ((MemberExpression)((Expression<Func<GalleryImage, bool>>)(s => s.is_album)).Body).Member.Name;
JToken jt;
if (!jo.TryGetValue(isAlbumPropertyName, StringComparison.InvariantCultureIgnoreCase, out jt))
{
return jo.ToObject<GalleryImage>();
}
var propValue = jt.Value<bool>();
if(propValue) {
resultType = typeof(GalleryAlbum);
}
else{
resultType = typeof(GalleryImage);
}
var resultObject = Convert.ChangeType(Activator.CreateInstance(resultType), resultType);
var objectProperties=resultType.GetProperties();
foreach (var objectProperty in objectProperties)
{
var propType = objectProperty.PropertyType;
var propName = objectProperty.Name;
var token = jo.GetValue(propName, StringComparison.InvariantCultureIgnoreCase);
if (token != null)
{
objectProperty.SetValue(resultObject,token.ToObject(propType)?? objectProperty.GetValue(resultObject));
}
}
return resultObject;
}
catch (Exception ex)
{
throw;
}
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
答案 3 :(得分:1)
布莱恩·罗杰斯的答案。关于“使用Serializer.Populate()代替item.ToObject()”。 如果派生类型具有contstructor或其某些类型具有自己的customconverter,则必须使用常规方法反序列化JSON。 因此,您必须将工作实例化为NewtonJson的新对象。这样,您可以在CustomJsonConverter中实现它:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
..... YOU Code For Determine Real Type of Json Record .......
// 1. Correct ContractResolver for you derived type
var contract = serializer.ContractResolver.ResolveContract(DeterminedType);
if (converter != null && !typeDeserializer.Type.IsAbstract && converter.GetType() == GetType())
{
contract.Converter = null; // Clean Wrong Converter grabbed by DefaultContractResolver from you base class for derived class
}
// Deserialize in general way
var jTokenReader = new JTokenReader(jObject);
var result = serializer.Deserialize(jTokenReader, DeterminedType);
return (result);
}
如果您有对象的递归,则可以使用此功能。
答案 4 :(得分:0)
我只发布此内容以消除一些混淆。如果您正在使用预定义的格式并需要对其进行反序列化,那么我发现这是最好的并且演示了这些机制,以便其他人可以根据需要进行调整。
System.Device.Location