如果在类中反序列化接口属性,如何定义采用哪个类?
想象一下您有以下类,
[DataContract]
public class NamedList
{
[DataMember]
public string Name { get; set; }
[DataMember]
public IList<string> Items { get; private set; }
public DumpList(string name)
{
Name = name;
Items = new List<string>();
}
}
如果将其序列化到文件中,则非常容易,因为 IList
背后的具体类是已知的并且可以序列化。
但是如果您尝试将此文件反序列化回内存中会发生什么?
它可以正常工作,不会发生任何直接错误。
如果您尝试在列表中添加或删除某些内容,就会出现问题。在这种情况下,你会得到一个例外。这个异常的根源来自于反序列化对象使用数组作为 IList 的具体实现的情况。
在这个简单的例子中避免这个问题很容易。只需序列化具体的后备存储而不是公共属性,并在构造函数中进行更改:
[DataMember(Name = "Items")]
private List<string> _Items;
public IList<string> Items
{
get
{
return _Items;
}
}
public DumpList(string name)
{
Name = name;
_Items = new List<string>();
}
但更有趣的问题是:
- 为什么选择 Deserializer Array 类型作为 IList 接口的具体实现?
- 是否可以更改每个接口应采用哪个类的设置?
- 如果我有一个自定义的接口和该接口的多个实现,是否可以告诉反序列化器应该为给定的接口采用哪个具体类?
Just imagine you have the following class
[DataContract]
public class NamedList
{
[DataMember]
public string Name { get; set; }
[DataMember]
public IList<string> Items { get; private set; }
public DumpList(string name)
{
Name = name;
Items = new List<string>();
}
}
If you serialize this into a file, it is quite easy, cause the concrete class behind the IList
is known and can be serialized.
But what happens if you try to deserialize this file back into memory?
It works without any direct error occuring.
The problem comes if you try to add or remove something from the list. In that case you'll get an exception. And the root of this exception comes from the case that the deserialized object uses as concrete implementation for the IList an Array.
To avoid this problem in this simple example is easy. Just serialize the concrete backing store instead of the public property and make the change in the constructor:
[DataMember(Name = "Items")]
private List<string> _Items;
public IList<string> Items
{
get
{
return _Items;
}
}
public DumpList(string name)
{
Name = name;
_Items = new List<string>();
}
But the more interesting question is:
- Why chooses the Deserializer the Array type as concrete implementation of the IList interface?
- Is it possible to change the settings which class should be taken for each interface?
- If i have a self defined interface and several implementations of this interface, is it possible to tell the Deserializer which concrete class should be taken for a given interface?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以使用 DataContractSurrogate 进行反序列化来解决此问题,将 IList 替换为 List。
基本上就是这样,您只需要使用该代理创建 DataContractSerializer 实例并将其用于反序列化(对于序列化来说并不重要),例如:
或任何其他采用代理的构造函数。
或者,(作为答案的奖励)如果您正在使用 app/web.config 定义的服务,您可以定义一个自定义行为,使用上述代理创建数据协定序列化程序:
<最后,您可以使用此行为:
要完成工作,请在某处调用上述 CreateService 来创建通道。
You can solve this using a DataContractSurrogate for the deserialization, that replaces IList with List.
Basically that's it, you just need to create your DataContractSerializer instance with that surrogate and use it for deserialization (for serialization it won't matter), for example:
Or any of the other constructors that take a surrogate.
Or, (as a bonus to the answer) if you're working with app/web.config-defined services, you can define a custom behavior that creates a data contract serializer with the above surrogate:
Finally you can use this behavior:
To finish the job, call the above CreateService somewhere to create the channel.
如果您使用
NetDataContractSerializer
,它将类型信息与序列化对象一起存储,您的问题应该得到解决。但是,它同时降低了与非 .NET 客户端的互操作性。If you use the
NetDataContractSerializer
, which stores type information along with the serialized object, your problem should be solved. However, it does at the same time reduce interoperability to non-.NET clients.