简体   繁体   English

从 JSON 反序列化对象的混合列表

[英]Deserializing a mixed list of objects from JSON

I'm using the DataContractJsonSerializer to deserialize objects from an external service.我正在使用DataContractJsonSerializer从外部服务反序列化对象。 In most cases, this has worked great for me.在大多数情况下,这对我来说效果很好。 However, there is one case where I need to deserialize JSON that contains a list of objects that all inherit from the same base class, but there are many different types of objects in that list.但是,在一种情况下,我需要反序列化 JSON,其中包含一个对象列表,这些对象都继承自同一个基础 class,但该列表中有许多不同类型的对象。

I know that it can be done easily by including a list of known types in the serializer's constructor, but I don't have access to the code that generated this JSON service.我知道可以通过在序列化程序的构造函数中包含已知类型的列表来轻松完成此操作,但我无法访问生成此 JSON 服务的代码。 The types that I'm using will be different from the types used in the service (mostly just the class name and namespace will be different).我使用的类型将与服务中使用的类型不同(主要是 class 名称和命名空间会不同)。 In other words, the classes that the data was serialized with will not be the same classes that I'll use to deserialize it even though they'll be very similar.换句话说,数据被序列化的类与我将用来反序列化它的类不同,即使它们非常相似。

With the XML DataContractSerializer , I can pass in a DataContractResolver to map the services types to my own types, but there is no such constructor for the DataContractJsonSerializer .使用 XML DataContractSerializer ,我可以将DataContractResolver传递给 map 服务类型到我自己的类型,但是DataContractJsonSerializer没有这样的构造函数。 Is there any way to do this?有没有办法做到这一点? The only options that I've been able to find are: write my own deserializer, or use Microsoft's JsonObject that isn't tested and "should not be used in production environments."我能找到的唯一选择是:编写自己的反序列化器,或者使用未经测试且“不应在生产环境中使用”的 Microsoft JsonObject

Here is an example:这是一个例子:

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

Here is the error mentioned in the comment above:这是上面评论中提到的错误:

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.

I found the answer.我找到了答案。 It was very simple.这很简单。 I just needed to update my DataContract attribute to specify which namespace (you can also specify a different name) they map to in the source JSON like this:我只需要更新我的DataContract属性以指定 map 到源 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; }
}

That JsonObject was a sample for .NET 3.5.该 JsonObject 是 .NET 3.5 的样本。 There is a project in codeplex - http://wcf.codeplex.com - which has a tested implementation of the JsonValue/JsonObject/JsonArray/JsonPrimitive classes, including source code and unit tests. codeplex 中有一个项目 - http://wcf.codeplex.com - 它具有 JsonValue/JsonObject/JsonArray/JsonPrimitive 类的经过测试的实现,包括源代码和单元测试。 With that you can parse "untyped" JSON.有了它,您可以解析“无类型”JSON。 Another well-used JSON framework is the JSON.NET at http://json.codeplex.com .另一个用得很好的 JSON 框架是 JSON.NET,位于http://json.codeplex.Z4D236D9A504102C50ZAD1504D102C50ZADBZ

You may create a DTO before serializing.您可以在序列化之前创建 DTO。

I use a class like: (pseudo code)我使用 class 之类的:(伪代码)

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

Using the JsonDto you can easily serialize arbitrary objects to JSON and deserialize them to their common base type because the deserializer will always know the original type and returns an type of object reference which will be casted if you use the generic Deserialize<T> method.使用JsonDto ,您可以轻松地将任意对象序列化为 JSON 并将它们反序列化为它们的公共基本类型,因为反序列化器将始终知道原始类型并返回 object 引用类型,如果您使用通用Deserialize<T>方法,它将被强制转换。

One caveat: If you set the Type property you should use the AssemblyQualifiedName of the type, however without the version attribute (ex: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly ).一个警告:如果您设置Type属性,您应该使用该类型的 AssemblyQualifiedName,但是没有版本属性(例如: MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly )。 If you just use the AssemblyQualifiedName property of the Type class you will end up with errors if your assembly version changes.如果您只使用 class TypeAssemblyQualifiedName属性,那么如果您的程序集版本发生更改,您最终会出现错误。

I implemented a JsonDtoCollection the same way, which derives from List<JsonDto> and provides methods to handle collections of objects.我以相同的方式实现了JsonDtoCollection ,它派生自List<JsonDto>并提供处理对象 collections 的方法。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM