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

时间:2014-07-09 02:24:29

标签: c# json json.net

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

{
  "Name":"foo",
  "widgetCollection":[{"name","foo"}, {"name","foo"},],
  "cogCollection": [{"name","foo"}, {"childCogs",<<new collection>>},],
}

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

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);
        }
    }
}

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

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

    public ICollection<ICog> childCogs { get; set; }        
}  

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

更新

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

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

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

 return objectType == typeof(ICog); 

遵循这种模式,你需要为你通过网络接收的每种抽象类型都有一个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);
    }
}

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

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

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

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

1 个答案:

答案 0 :(得分:25)

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

var settings = new JsonSerializerSettings();
settings.Converters.Add(new CogConverter());

您的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);
    }
}

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

修改

进一步阅读后,您似乎可能正在寻找具体如何使用IoC创建的ICog用于填充。我正在使用以下内容作为我的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;

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