从 JSON 反序列化混合对象列表
我使用 DataContractJsonSerializer
从外部服务反序列化对象。在大多数情况下,这对我来说非常有效。但是,在一种情况下,我需要反序列化 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.
I'm using the DataContractJsonSerializer
to deserialize objects from an external service. 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.
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. 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). 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
. 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."
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我找到了答案。很简单。我只需要更新我的
DataContract
属性来指定它们在源 JSON 中映射到的命名空间(您也可以指定不同的名称),如下所示: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:该 JsonObject 是 .NET 3.5 的示例。 codeplex 中有一个项目 - http://wcf.codeplex.com - 它具有经过测试的 JsonValue 实现/JsonObject/JsonArray/JsonPrimitive 类,包括源代码和单元测试。这样你就可以解析“无类型”JSON。另一个常用的 JSON 框架是 JSON.NET,位于 http://json.codeplex.com。
That JsonObject was a sample for .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. With that you can parse "untyped" JSON. Another well-used JSON framework is the JSON.NET at http://json.codeplex.com.
您可以在序列化之前创建 DTO。
我使用如下类:(伪代码)
使用
JsonDto
您可以轻松地将任意对象序列化为 JSON 并将它们反序列化为其公共基本类型,因为反序列化器将始终知道原始类型并返回类型如果您使用通用Deserialize
方法,将强制转换对象引用。需要注意的是:如果设置
Type
属性,则应使用该类型的 AssemblyQualifiedName,但不使用版本属性(例如:MyCompany.SomeNamespace.MyType、MyCompany.SomeAssembly
) 。如果您只使用Type
类的AssemblyQualifiedName
属性,那么如果您的程序集版本发生更改,您最终会遇到错误。我以相同的方式实现了
JsonDtoCollection
,它派生自List
并提供了处理对象集合的方法。You may create a DTO before serializing.
I use a class like: (pseudo code)
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 genericDeserialize<T>
method.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
). If you just use theAssemblyQualifiedName
property of theType
class you will end up with errors if your assembly version changes.I implemented a
JsonDtoCollection
the same way, which derives fromList<JsonDto>
and provides methods to handle collections of objects.