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

时间:2011-05-24 19:03:57

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

我正在使用DataContractJsonSerializer从外部服务反序列化对象。在大多数情况下,这对我来说非常有用。但是,有一种情况我需要反序列化包含所有从同一基类继承的对象列表的JSON,但该列表中有许多不同类型的对象。

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

使用XML DataContractSerializer,我可以传入DataContractResolver将服务类型映射到我自己的类型,但DataContractJsonSerializer没有这样的构造函数。 有没有办法做到这一点?我能找到的唯一选择是:编写我自己的反序列化器,或使用未经测试的Microsoft's 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.

3 个答案:

答案 0 :(得分: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; }
}

答案 1 :(得分:0)

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

答案 2 :(得分:0)

您可以在序列化之前创建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)。如果您只使用AssemblyQualifiedName类的Type属性,则在程序集版本更改时最终会出错。

我以同样的方式实现了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