可以在此处查看复制:Gist。
在尝试反序列化接口集合时收到错误,尤其是在接口还包含相同接口集合的情况下。如果删除嵌套的集合,则可以反序列化。我以为实现JsonConverter可以解决此问题,但我似乎误会了。
请注意,我无法控制序列化。
我收到的错误是:
无法创建类型为JsonRepro.IMember的实例。类型是接口或抽象类,无法实例化。路径“ members [0] .id”,第17行,位置25。
最小复制如下:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonRepro
{
public interface IMember
{
int ID { get; set; }
string Type { get; set; }
}
public class A : IMember
{
public int ID { get; set; }
public string Type { get; set; }
public List<IMember> Members { get; set; }
}
public class B : IMember
{
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class MyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IMember).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
JObject item = JObject.Load(reader);
var typeValue = item["type"].Value<string>();
switch (typeValue)
{
case "A":
return item.ToObject<A>();
case "B":
return item.ToObject<B>();
}
throw new NotSupportedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite => false;
}
class Program
{
private const string json = @"
[
{
'id': 0,
'type': 'A'
},
{
'id': 1,
'type': 'B',
'name': 'One'
},
{
'id': 2,
'type': 'A',
'members': [
{
'id': 3,
'type': 'A'
}
]
}
]
";
static void Main(string[] args)
{
var settings = new JsonSerializerSettings() { Converters = { new MyConverter() } };
var result = JsonConvert.DeserializeObject<List<IMember>>(json, settings);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
答案 0 :(得分:0)
如果同时控制序列化和反序列化,请尝试在序列化程序设置中同时使用TypeNameHandling.All
和反序列化。
当您具有接口之类的抽象类型时,它不知道要使用哪种具体类型。外部具体类型可以在Deserialize
类型参数中指定,但是内部类型不能指定。使用TypeNameHandling
可以反序列化为抽象类型,但实际上它将反序列化为$type
中指定的具体类型。
另一种方法是将反序列化为JObject
,然后对每个反序列化ToObject<T>
,但随后必须对每个内部对象进行反序列化。
您还可以使用自定义JsonConverter
,但这更加复杂。
另一种替代方法是,只要可以避免,就不要使用接口来存储数据。如果只是一堆没有方法的属性,则可能不需要接口。
更新:
如何对您的自定义JsonConverter
进行修改:
case "A":
return new A
{
ID = item["id"]?.Value<int>() ?? 0,
Type = typeValue,
Members = ((JArray)item["members"])?.ToObject<List<IMember>>(serializer)
};
虽然不漂亮,但是可以。您需要在ToObject
上将序列化程序与自定义转换器一起传递,但是如果执行item.ToObject<A>(serializer)
则会导致堆栈溢出,因此只需要在members
属性上进行。>