【问题标题】:Type is an interface or abstract class and cannot be instantiated类型是接口或抽象类,不能实例化
【发布时间】:2014-07-09 02:24:29
【问题描述】:

我会先说我知道问题出在哪里,只是不知道如何解决它。我正在与以 JSON 形式返回数据的 .NET SOA 数据层进行通信。一种这样的方法返回一个包含多个集合的对象。对象基本上是这样的:

{
  "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 GenericJsonConverter(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) 在这种情况下会给你一个可疑的演员表警告。

希望其他人在以后发现这很有用!

【问题讨论】:

标签: c# json json.net


【解决方案1】:

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

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。如果序列化程序不能负责实际构造Cog 本身,您可能还需要考虑授予CogConverter 对容器的访问权限;这一切都取决于您的特定架构。

编辑

进一步阅读后,您似乎正在寻找具体如何使用 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 方法中使用您希望的自定义​​类型。

【讨论】:

  • 通过一点工作,这是正确的答案。不过我很好奇,你的“DetermineConcreteType”方法是做什么的?
  • 在我们的版本中,我们查看其上的一些属性以确定正确的子类型(我们的接口与实现不是 1:1 的)——我能想到的通用类型是下拉指示付款方式(信用卡与银行等) - 每个都有不同的字段,但下拉列表的值可以指示该类型。如果您有想要分享的代码作为答案的一部分,我很乐意更新我的代码以包含更完整的解决方案。
  • 酷,谢谢分享!我建议使用 DI 的 JsonSerializerSettings 而不是直接在之前构建它;您可以使用您的容器测试 JsonSerializerSettings 注册,以确保其中包含适当的转换器,不必担心可疑的演员表,并且仍然能够独立地使用泛型测试转换器。
  • 非常感谢您指出这一点!真的很有帮助! (我需要接口以便我可以模拟类,在反序列化时遇到异常,然后遇到这个问答)很好的一个! +1
  • 很高兴它仍在帮助人们,@bas!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
相关资源
最近更新 更多