在JSON.NET

时间:2017-05-30 11:34:56

标签: c# json json.net deserialization

我有一些我希望Deserialise的json。反序列化很容易回到List但是我正在使用接口,这就是我需要反序列化最终的结果:

所以让我们从一些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);

我总是返回null。

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

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

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

正常工作。

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

此致

CHUD

PS - 我已经改变了类名/属性等,远离我正在做的实际现实世界的实现。所以,如果他们出现错误,我会道歉。但希望大多数人都可以,并且理解点/终极目标:)

4 个答案:

答案 0 :(得分:3)

问题

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

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

您的错误清楚地表明:

  

无法创建BLA类型的实例。 Type是接口或抽象类,无法实例化“

解决方案

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

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

更多Belabor

你可能会问“为什么我需要施放”?为什么我不能这样做呢?

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

preserve reference identity不允许这样做。

答案 1 :(得分:1)

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

问题在于:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);
  • JsonConvert将处理您提供的json数据。
  • 因为要求将其转换为IList<IRootObject>类型的对象,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> MyDeserialisedRootObjects = JsonConvert.Deserialise<List<RootObject>>(JsonString);

如果您想将List<RootObject>变成IList<IRootObject>

IList<IRootObject> MyDeserialisedIRootObjects = MyDeserialisedRootObjects.ToList<IRootObject>();

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

IList<IRootObject> MyDeserialisedIRootObjects = MyDeserialisedRootObjects .Cast<IRootObject>().ToList();

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

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

答案 2 :(得分:0)

我相信你永远不会使用自定义转换器。根据Newtonsoft.Json文档(here),你应该使用这样的东西:

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

答案 3 :(得分:0)

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


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

相关问题