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

本文关键字:混合 列表 对象 反序列化 JSON | 更新日期: 2023-09-27 17:57:39

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

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

使用XML DataContractSerializer,我可以传入DataContractResolver来将服务类型映射到我自己的类型,但DataContractJsonSerializer没有这样的构造函数<有办法做到这一点吗>我能找到的唯一选项是:编写自己的反序列化程序,或者使用未经测试且"不应在生产环境中使用"的Microsoft JsonObject

这里有一个例子:

[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.

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

我找到了答案。这很简单。我只需要更新我的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; }
}
JsonObject是.NET 3.5的一个示例。codeplex中有一个项目-http://wcf.codeplex.com-它测试了JsonValue/JsonObject/JsonArray/JsonPrimitive类的实现,包括源代码和单元测试。有了它,您可以解析"非类型化"的JSON。另一个使用良好的JSON框架是位于http://json.codeplex.com.

您可以在序列化之前创建一个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,并将它们反序列化为它们的公共基类型,因为反序列化器将始终知道原始类型,并返回一种对象引用类型,如果您使用通用Deserialize<T>方法,该对象引用类型将被强制转换。

需要注意的一点是:如果设置Type属性,则应使用该类型的AssemblyQualifiedName,但不使用version属性(例如:MyCompany.SomeNamespace.MyType, MyCompany.SomeAssembly)。如果只使用Type类的AssemblyQualifiedName属性,那么如果程序集版本发生更改,则最终会出现错误。

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

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