【问题标题】:Deserialise IList<Interface> in JSON.NET在 JSON.NET 中反序列化 IList<Interface>
【发布时间】:2017-10-30 20:46:15
【问题描述】:

我有一些想要反序列化的 json。反序列化回列表很容易但是我正在使用接口,这就是我需要反序列化的结果:

让我们从一些 JSON 开始:

[{"TimeGenerated":"2017-05-30T19:21:06.5331472+10:00","OuterValue":1.08,"Identifier":{"IdentifierName":"BLA","IdentifierNumber":1},"Locale":{"LocaleName":"LOCALE NAME","LocaleType":1,"LocaleNumber":1,"StartTime":"2017-05-30T19:20:00+10:00"},"InnerValue":1.08,"InnerType":0,"InnerId":"InnerI","Type":"MyType","Id":"11111"}]

这是在服务器端序列化的数据(当然没有问题),它的对象类型是:

IList<IRootObject>

如此有效,我想将另一端反序列化为:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

显然这里的pickle是旧的接口/抽象类型错误:

'Could not create an instance of type BLA. Type is an interface or abstract class and cannot be instantiated'

好的,我创建了一些自定义转换器并进行如下装饰:

类是:

    [JsonObject(MemberSerialization.OptIn)]
    public class Identifier
    {
        [JsonProperty]
        public string IdentifierName { get; set; }
        [JsonProperty]
        public int IdentifierNumber { get; set; }
    }
    [JsonObject(MemberSerialization.OptIn)]
    public class Locale
    {
        [JsonProperty]
        public string LocaleName { get; set; }
        [JsonProperty]            
        public int LocaleType { get; set; }
        [JsonProperty]            
        public int LocaleNumber { get; set; }
        [JsonProperty]
        public string StartTime { get; set; }
    }

    [JsonObject(MemberSerialization.OptIn)]
    public class RootObject:IRootObject
    {
       [JsonProperty]
       public string TimeGenerated { get; set; }
       [JsonProperty]        
       public double OuterValue { get; set; }
       [JsonProperty] 
       [JsonConverter(typeof(ConcreteConverter<Identifier>))]
       public IIdentifier Identifier { get; set; }
       [JsonProperty]
       [JsonConverter(typeof(ConcreteConverter<Locale>))]
       public ILocale Locale { get; set; }
       [JsonProperty]
       public double InnerValue { get; set; }
       [JsonProperty]
       public int InnerType { get; set; }
       [JsonProperty]
       public string InnerId { get; set; }
       [JsonProperty]
       public string Type { get; set; }
       [JsonProperty]
       public string Id { get; set; }
    }

界面是:

public interface IRootObject
{
    string TimeGenerated { get; set; }
    double OuterValue { get; set; }
    Identifier Identifier { get; set; }
    Locale Locale { get; set; }
    double InnerValue { get; set; }
    int InnerType { get; set; }
    string InnerId { get; set; }
    string Type { get; set; }
    string Id { get; set; }
}

具体转换器如下:

public class ConcreteConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    try
    {
        var s = serializer.Deserialize<T>(reader);
        return s;
    }
    catch (Exception ex)
    {
        return null;
    }
    // return serializer.Deserialize<T>(reader);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    serializer.Serialize(writer, value);
}

}

我还制作了一个列表转换器(看到这是一个列表,虽然它没有使用,但这里需要 JIC):

public class ConcreteListConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (existingValue == null)
        return null;

    var res = serializer.Deserialize<List<TImplementation>>(reader);
    return res.ConvertAll(x => (TInterface)x);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    serializer.Serialize(writer, value);

}

}

所以问题是:

当我跑步时:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

我总是返回一个空值。

当我在我的 ConcreteConverter CanConvert/ReadJson/WriteJson 方法上设置断点时 - 它们永远不会被命中。

我缺少什么,以及如何获得以下魔力:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

正常工作。

我必须注意,我不想只是 typnamehandling.objects(我认为就是这样),它将 ASSEMBLYNAME.TYPE 等添加到 json 中,从而使其膨胀。假设上面的 json 是我们必须使用的,上面表示的类及其接口是它必须反序列化的。

问候,

查德

PS - 我已经改变了类名/属性等,使其远离我正在做的实际现实世界的实现。因此,如果他们出现错误,我深表歉意。但希望大多数人都没事,并且理解重点/最终目标:)

【问题讨论】:

  • 您可能没有发布所有代码,但是您在哪里/如何告诉 JsonConvert 使用您的自定义转换器?
  • @Camilo 我现在离开电脑了。想一想,我不确定我是否是:S 你能否发布一下你将如何做到这一点。谢谢。
  • 看我的回答,可能就是你想要的

标签: c# json json.net deserialization


【解决方案1】:

问题

您不能创建接口的实例,这在 .NET 中是不允许的。你不能这样做:

var items = new IList<int>(); // Not allowed

您的错误清楚地表明:

无法创建 BLA 类型的实例。类型是接口或抽象类,不能实例化'

解决方案

将其反序列化为类型List,但将其分配给类型IList。请注意,右侧的类型不是下面代码中的接口,除非在铸造阶段。

IList<IRootObject> MyDeserialisedObject = 
            JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString))
            .Cast<IRootObject>().ToList();

更多的麻烦

你可能会问“为什么我需要投射”,为什么我不能这样做?

IList<IRootObject> MyDeserialisedObject = 
            JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString));

这是不允许的preserve reference identity

【讨论】:

    【解决方案2】:

    我相信您从未使用过您的自定义转换器。根据 Newtonsoft.Json 文档 (here),您应该使用如下内容:

    IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString, new ConcreteListConverter());
    

    【讨论】:

      【解决方案3】:

      您正试图让 JsonConvert 实例化接口,这是不可能的。

      问题出在这里:

      IList<IRootObject> MyDeserializedObject = JsonConvert.Deserialize<IList<IRootObject>>(JsonString);
      
      • JsonConvert 将处理您提供给它的 json 数据。
      • 因为您要求将其转换为 IList&lt;IRootObject&gt; 类型的对象,JsonConvert 将迭代地构建 IRootObject 对象的列表。
      • 对于列表中的每个元素(在 json 数据字符串中),JsonConvert 将创建一个 new IRootObject() 并使用它在 json 数据中找到的数据填充其属性。

      这就是问题所在:new IRootObject() 不可能,因为您无法实例化接口。您只能实例化类。

      解决方案

      你已经有一个班级RootObject。它已经包含您的界面包含的相同字段。但是,RootObject 目前没有实现IRootObject

      将接口实现添加到类中:

      public class RootObject : IRootObject
      {
          //make sure the implementation is correct
      }
      

      然后你可以让 JsonConvert 给你一个RootObject 的列表(不是IRootObject!)

      List<RootObject> MyDeserializedRootObjects = JsonConvert.Deserialize<List<RootObject>>(JsonString);
      

      如果你想把List&lt;RootObject&gt; 变成IList&lt;IRootObject&gt;

      IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects.ToList<IRootObject>();
      

      如果您使用的是 .NET 3.5 或更低版本,您可以按如下方式转换列表:

      IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects .Cast<IRootObject>().ToList();
      

      编辑我可能做得有点偷偷摸摸,但出于同样的原因,你不能实例化IRootObject,你也不能实例化IList。这两个都是接口。

      两者的解决方案是相同的:实例化一个实现接口ListRootObject)的类的对象,然后将它们转换为它们的接口 em>(IListIRootObject)。

      【讨论】:

      • 您好,fwiw - rootobject 实现了 irootobject 接口。只是一个错字。已编辑。现在将阅读您的帖子的其余部分,谢谢。
      • 只是为了澄清 - 我可以很容易地选择你的回答作为答案。但是为了公平起见,让 CodingYoshi 首先对演员做出回应,我选择了他的,尽管你的回答实际上总体上更全面:) 作为新手,我希望我做对了。
      • @The_Chud:这里有点像汉兰达。只能有一个:)
      【解决方案4】:

      忽略i++,这只是我对数据库列的迭代。我的reader 正在从数据库中读取一个 Student 类型的 JSON 对象,我使用了这样的 IList 并且它起作用了。如果它提供更清晰的说明,IList 是一个接口,List 是一个具体类或更好,而 List 是实现 IList 接口的具体类型。

      
      course.Student = reader.DeserializeObject<IList<Student>>(i++);
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-18
        相关资源
        最近更新 更多