C# 使用json.net反序列化无类型信息的多态json类
此调用返回一个列表,其中包含以JSON表示的画廊图像和画廊相册类 我看不出如何使用Json.NET自动反序列化这些类,因为没有$type属性告诉反序列化程序要表示哪个类。有一个称为“IsAlbum”的属性可以用来区分两者 这个问题似乎显示了一种方法,但它看起来有点像黑客 如何反序列化这些类?(使用C#,Json.NET) 样本数据: 画廊图片C# 使用json.net反序列化无类型信息的多态json类,c#,serialization,json.net,imgur,C#,Serialization,Json.net,Imgur,此调用返回一个列表,其中包含以JSON表示的画廊图像和画廊相册类 我看不出如何使用Json.NET自动反序列化这些类,因为没有$type属性告诉反序列化程序要表示哪个类。有一个称为“IsAlbum”的属性可以用来区分两者 这个问题似乎显示了一种方法,但它看起来有点像黑客 如何反序列化这些类?(使用C#,Json.NET) 样本数据: 画廊图片 { "id": "OUHDm", "title": "My most recent drawing. Spent over 100 hou
{
"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"
}
]
}
}
通过创建一个定制的
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 jo = JObject.Load(reader);
// Using a nullable bool here in case "is_album" is not present on an item
bool? isAlbum = (bool?)jo["is_album"];
GalleryItem item;
if (isAlbum.GetValueOrDefault())
{
item = new GalleryAlbum();
}
else
{
item = new GalleryImage();
}
serializer.Populate(jo.CreateReader(), item);
return item;
}
public override bool CanWrite
{
get { return false; }
}
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();
}
}
}
Fiddle:我发布这篇文章只是为了澄清一些困惑。如果您使用的是预定义格式,并且需要对其进行反序列化,我发现这是最有效的方法,并演示了机制,以便其他人可以根据需要对其进行调整
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);
}
}
公共类BaseClassConverter:JsonConverter
{
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
var j=作业对象加载(读卡器);
var retval=BaseClass.From(j,序列化程序);
返回返回;
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
serializer.Serialize(writer,value);
}
公共覆盖布尔CanConvert(类型objectType)
{
//重要信息-不要使子类通过此转换器
返回objectType==typeof(基类);
}
}
//重要的是不要使用属性,否则将导致无限循环
公共抽象类基类
{
内部静态类型[]类型=新类型[]{
类型(第1子类),
类型(第2子类),
类型(第3子类)
};
内部静态字典TypesByName=Types.ToDictionary(t=>t.Name.Split('.').Last());
//基于类名的类型属性
[JsonProperty(PropertyName=“type”,Required=Required.Always)]
公共字符串JsonObjectType{get{返回此.GetType().Name.Split('.').Last();}set{}
//反序列化作业对象的简便方法
来自(JObject obj、JsonSerializer序列化程序)的公共静态新基类
{
//这是我们的对象类型属性
var str=(字符串)obj[“type”];
//我们使用字典绘制地图,但你可以做任何你想做的事
var type=TypesByName[str];
//传递序列化程序(及其设置)很重要
返回obj.ToObject(类型,序列化器)作为基类;
}
//反序列化的简便方法
公共静态基类反序列化(JsonReader)
{
JsonSerializer ser=新的JsonSerializer();
//此处添加转换器很重要
添加(新的BaseClassConverter());
返回序列反序列化(读取器);
}
}
以下实现应允许您反序列化,而无需更改类的设计方式,也无需使用$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();
}
}
公共类GalleryImageConverter:JsonConverter
{
公共覆盖布尔CanConvert(类型objectType)
{
return(objectType==typeof(GalleryImage)| | objectType==typeof(GalleryAlbum));
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
尝试
{
如果(!CanConvert(对象类型))
抛出新的InvalidDataException(“对象类型无效”);
JObject jo=JObject.Load(读卡器);
//以下是避免使用魔术字符串
var isAlbumPropertyName=((MemberExpression)((Expression)(s=>s.is_相册)).Body.Member.Name;
jtokenjt;
if(!jo.TryGetValue(IsalBumpPropertyName,StringComparison.InvariantCultureIgnoreCase,out jt))
{
返回jo.ToObject();
}
var propValue=jt.Value();
if(propValue){
结果类型=类型(GalleryAlbum);
}
否则{
结果类型=类型(GalleryImage);
}
var resultObject=Convert.ChangeType(Activator.CreateInstance(resultType),resultType);
var objectProperties=resultType.GetProperties();
foreach(objectProperties中的var objectProperty)
{
var propType=objectProperty.PropertyType;
var propName=objectProperty.Name;
var token=jo.GetValue(propName、StringComparison.InvariantCultureIgnoreCase);
if(令牌!=null)
{
objectProperty.SetValue(resultObject,token.ToObject(propType)??objectProperty.GetValue(resultObject));
}
}
返回结果对象;
}
捕获(例外情况除外)
{
投掷;
}
}
公共覆盖布尔可写
{
获取{return false;}
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新的NotImplementedException();
}
}
仅使用与Json.NET一起工作的属性
[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; }
}
[JsonConverter(typeof(JsonSubtypes),“is_相册”)]
[JsonSubtypes.KnownSubType(typeof(GalleryAlbum),true)]
[JsonSubtypes.KnownSubType(typeof(GalleryImage),false)]
公共抽象类GalleryItem
{
[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; }
}
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);
}