【问题标题】:Deserializing a mixed list of objects from JSON从 JSON 反序列化对象的混合列表
【发布时间】:2011-05-24 19:03:57
【问题描述】:

我正在使用DataContractJsonSerializer 反序列化来自外部服务的对象。在大多数情况下,这对我来说效果很好。但是,在一种情况下,我需要反序列化 JSON,其中包含一个对象列表,这些对象都继承自同一个基类,但该列表中有许多不同类型的对象。

我知道可以通过在序列化程序的构造函数中包含已知类型的列表来轻松完成此操作,但我无法访问生成此 JSON 服务的代码。我使用的类型与服务中使用的类型不同(主要是类名和命名空间不同)。换句话说,序列化数据的类与我将用来反序列化它的类不同,即使它们非常相似。

使用 XML DataContractSerializer,我可以传入 DataContractResolver 以将服务类型映射到我自己的类型,但 DataContractJsonSerializer 没有这样的构造函数。 有没有办法做到这一点? 我能找到的唯一选择是:编写我自己的反序列化器,或使用未经测试且“不应在生产环境。”

这是一个例子:

[DataContract]
public class Person
{
    [DataMember]
    public string Name { get; set; }
}

[DataContract]
public class Student : Person
{
    [DataMember]
    public int StudentId { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var jsonStr = "[{\"__type\":\"Student:#UnknownProject\",\"Name\":\"John Smith\",\"StudentId\":1},{\"Name\":\"James Adams\"}]";

        using (var stream = new MemoryStream())
        {
            var writer = new StreamWriter(stream);
            writer.Write(jsonStr);
            writer.Flush();

            stream.Position = 0;
            var s = new DataContractJsonSerializer(typeof(List<Person>), new Type[] { typeof(Student), typeof(Person) });
            // Crashes on this line with the error below
            var personList = (List<Person>)s.ReadObject(stream);
        }
    }
}

这是上面评论中提到的错误:

Element ':item' contains data from a type that maps to the name
'http://schemas.datacontract.org/2004/07/UnknownProject:Student'. The
deserializer has no knowledge of any type that maps to this name. Consider using
a DataContractResolver or add the type corresponding to 'Student' to the list of
known types - for example, by using the KnownTypeAttribute attribute or by adding
it to the list of known types passed to DataContractSerializer.

【问题讨论】:

    标签: c# .net json .net-4.0 deserialization


    【解决方案1】:

    我找到了答案。这很简单。我只需要更新我的 DataContract 属性以指定它们在源 JSON 中映射到的命名空间(您也可以指定不同的名称),如下所示:

    [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject")]
    public class Person
    {
        [DataMember]
        public string Name { get; set; }
    }
    
    [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/UnknownProject"]
    public class Student : Person
    {
        [DataMember]
        public int StudentId { get; set; }
    }
    

    【讨论】:

      【解决方案2】:

      该 JsonObject 是 .NET 3.5 的示例。 codeplex 中有一个项目 - http://wcf.codeplex.com - 它具有 JsonValue/JsonObject/JsonArray/JsonPrimitive 类的经过测试的实现,包括源代码和单元测试。有了它,您可以解析“无类型”的 JSON。另一个使用良好的 JSON 框架是 JSON.NET,http://json.codeplex.com

      【讨论】:

        【解决方案3】:

        您可以在序列化之前创建一个 DTO。

        我使用类似的类:(伪代码)

        class JsonDto
        
        string Content {get;set;}
        string Type {get;set;}
        
        ctor(object) => sets Content & Type Properties
        
        static JsonDto FromJson(string) // => Reads a Serialized JsonDto 
                                        //    and sets Content+Type Properties
        
        string ToJson() // => serializes itself into a json string
        
        object Deserialize() // => deserializes the wrapped object to its saved Type
                             //    using Content+Type properties
        
        T Deserialize<T>()   // deserializes the object as above and tries to cast to T
        

        使用JsonDto,您可以轻松地将任意对象序列化为 JSON 并将它们反序列化为它们的公共基类型,因为反序列化器将始终知道原始类型并返回一个对象引用类型,如果您使用通用 @ 则该类型将被强制转换987654323@方法。

        一个警告:如果您设置Type 属性,您应该使用该类型的AssemblyQualifiedName,但是没有版本属性(例如:MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly)。如果您只使用 Type 类的 AssemblyQualifiedName 属性,那么如果您的程序集版本发生更改,您最终会出错。

        我以同样的方式实现了JsonDtoCollection,它派生自List&lt;JsonDto&gt;,并提供了处理对象集合的方法。

        class JsonDtoCollection : List<JsonDto>
        
        ctor(List<T>) => wraps all items of the list and adds them to itself
        
        static JsonDtoCollection FromJson(string) // => Reads a collection of serialized
                                                  //    JsonDtos and deserializes them, 
                                                  //    returning a Collection
        
        string ToJson() // => serializes itself into a json string
        
        List<object> Deserialize() // => deserializes the wrapped objects using
                                   //    JsonDto.Deserialize
        
        List<T> Deserialize<T>()   // deserializes the as above and tries to cast to T
        

        【讨论】:

          猜你喜欢
          • 2019-04-05
          • 1970-01-01
          • 1970-01-01
          • 2015-07-27
          • 2014-12-01
          • 2019-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多