Type是接口或抽象类,无法实例化

dpa*_*ons 24 c# json json.net

我将在前言中说我知道问题是什么,我只是不知道如何解决它.我正在与.NET SOA数据层进行通信,该数据层将数据作为JSON返回.一种这样的方法返回一个在其中包含多个集合的对象.该对象基本上如下所示:

{
  "Name":"foo",
  "widgetCollection":[{"name","foo"}, {"name","foo"},],
  "cogCollection": [{"name","foo"}, {"childCogs",<<new collection>>},],
}
Run Code Online (Sandbox Code Playgroud)

我代表这个对象的类看起来像这样:

public class SuperWidget : IWidget
{
    public string Name { get; set; }

    public ICollection<IWidget> WidgetCollection { get; set; }
    public ICollection<ICog> CogCollection { get; set; }

    public SuperWidget()
    {
    }

    [JsonConstructor]
    public SuperWidget(IEnumerable<Widget> widgets, IEnumerable<Cog> cogs)
    {
        WidgetCollection = new Collection<IWidget>();
        CogCollection = new Collection<ICog>();

        foreach (var w in widgets)
        {
            WidgetCollection.Add(w);
        }
        foreach (var c in cogs)
        {
            CogCollection.Add(c);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个构造函数工作正常,直到cogCollection添加了一个子集合,现在我收到了上面的错误.具体的cog类看起来像这样:

[Serializable]
public class Cog : ICog
{
    public string name { get; set; }

    public ICollection<ICog> childCogs { get; set; }        
}  
Run Code Online (Sandbox Code Playgroud)

我不想将集合更改为具体类型,因为我正在使用IoC.因为我正在使用IoC,我真的想摆脱需要使用具体参数的JsonConstructors,但我还没有想出办法来做到这一点.任何建议将不胜感激!

更新:

Yuval Itzchakov建议这个问题可能是重复的,这似乎有点真实(似乎).在引用的帖子中,页面中的一个答案提供了此处提供的相同解决方案.我没有注意到答案,因为OP的问题与我在这里的问题不同.我的错.

-------我的解决方案--------

正如我所说:Matt的解决方案需要一些工作,但我得到了适合我的设置.关于他的初始解决方案,我不喜欢的一件事是这样的线:

 return objectType == typeof(ICog); 
Run Code Online (Sandbox Code Playgroud)

遵循这种模式,您需要为通过网络接收的每种抽象类型都有一个JsonConverter.这在我的情况下不太理想,所以我创建了一个通用的JsonConverter:

public class GenericJsonConverter<T>: JsonConverter, IBaseJsonConverter<T>
{
    private readonly IUnityContainer Container;
    public TalonJsonConverter(IUnityContainer container)
    {
        Container = container;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
        var result = Container.Resolve<T>();
        serializer.Populate(target.CreateReader(), result);
        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后就在我反序列化我的数据之前,我做了类似这样的事情:

 var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
 settings.Converters.Add((JsonConverter)Container.Resolve<IBaseJsonConverter<ICog>>());
Run Code Online (Sandbox Code Playgroud)

Protip:如果你使用Resharper,(JsonConverter)会在这种情况下给你一个可疑的投射警告.

希望其他人在路上找到这个有用的东西!

Mat*_*rey 27

您需要为Json.Net提供自定义序列化程序,以告诉它如何处理子嵌入式.例如:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new CogConverter());
Run Code Online (Sandbox Code Playgroud)

CogConverter需要继承JsonConverter并指定它是CanConvert您的ICog界面.也许是这样的:

public class CogConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ICog);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, typeof(Cog));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

JsonSerializerSettings在这种情况下,我建议您使用IoC容器进行注册.CogConverter如果序列化程序不负责实际构建Cog自身,您可能还需要考虑提供对容器的访问权限; 这一切都取决于您的特定架构.

编辑

在进一步阅读时,您似乎可能正在寻找具体如何使用ICog为人口创建的IoC .我正在使用以下内容作为我的ReadJson的一部分:

var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
var objectType = DetermineConcreteType(target);
var result = iocContainer.Resolve(objectType);
serializer.Populate(target.CreateReader(), result);
return result;
Run Code Online (Sandbox Code Playgroud)

这允许您使用任何对象并从原始JSON填充它,使用您希望在DetermineConcreteType方法中的自定义类型.