我遇到过这样的情况:我使用 NetDataContractSerializer 序列化一些 .NET 对象,并将 XML 存储在数据库中,作为记住应用程序中这些对象的状态的一种方式。最近我刚刚遇到第一种情况,即属性和类型名称的某些代码重构导致无法反序列化此 XML 数据。
到目前为止,我已经针对如何处理版本兼容性中断提出了两种不同的攻击计划,例如使用 NetDataContractSerializer 本身内可用的设施来控制反序列化或直接转换 XML。根据我的实验和研究,似乎可以使用自定义SerializationBinder http://msdn.microsoft.com/en-us/library/ee358766.aspx属性名称/类型更改可以通过实现 ISerialized 或通过实现 ISurrogateSelector 和 ISerializationSurrogate 编写序列化代理来解决。不幸的是,这种首选机制尚未成功,除非我可以证明,否则使用 NetDataContractSerializer 不可能使用代理在序列化数据的版本之间移动,这是由于一些无法解释的原因微软的设计决策 https://connect.microsoft.com/VisualStudio/feedback/details/678627/iserializationsurrogate-loaded-from-isurrogateselector-in-netdatacontractserializer-receives-no-serializationinfo。微软建议的是在两侧使用相同的序列化,这完全违背了使用代理来帮助类型名称更改或移动到不同的命名空间或程序集中的情况的目的。
要修复此问题,请使用相同的 NetDataContractSerializer 实例或
另一个实例也用兼容的初始化
代理选择器。
这个解释与MSDN 文章 http://msdn.microsoft.com/en-us/magazine/cc188950.aspx其中涉及使用自定义绑定器来替换类型以及处理序列化结构中的其他更改。
在反序列化期间,格式化程序会发现绑定程序已设置。
当每个对象即将被反序列化时,格式化程序调用
binder 的 BindToType 方法,向其传递程序集名称和类型
格式化程序想要反序列化。此时,BindToType 决定
实际应该构造什么类型并返回该类型。
请注意,原始类型和新类型必须具有完全相同的字段
名称和类型(如果新类型通过以下方式使用简单序列化)
可序列化的自定义属性。然而,新版本的类型
可以实现 ISerialized 接口及其特殊的
构造函数将被调用,类型可以检查中的值
SerializationInfo 对象并确定如何反序列化自身。
因此,要么我能够让 NetDataContractSerializer 将 V1 XML 反序列化为 V2 类型,要么我必须手动转换 XML。如果有人能够证明 NetDataContractSerializer 的 SerializationInfo 在使用 ISerialized 或使用序列化代理时确实有效,那将非常好,或者至少给出比 Microsoft 给出的更好的解释,否则我可能会发布一个新问题来辩论最佳方法在.NET中直接转换旧的XML。
2011 年 8 月 16 日更新:经过一些实验后发现,如果序列化的原始类型实现了 ISerialized,则 ISerialized 和序列化代理技术都可以正常工作,否则如果该类型仅使用 [Serialized] 属性,则对象图中的每个字段似乎都缺少一些有价值的类型信息以额外属性的形式。
使用示例[可序列化]属性
<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest">
<_stable z:Id="2">Remains the same</_stable>
<_x003C_OldProperty_x003E_k__BackingField>23</_x003C_OldProperty_x003E_k__BackingField>
</OldClass2>
实施示例可串行化:
<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest">
<_stable z:Id="2" z:Type="System.String" z:Assembly="0" xmlns="">Remains the same</_stable>
<_x003C_OldProperty_x003E_k__BackingField z:Id="3" z:Type="System.Int32" z:Assembly="0" xmlns="">23</_x003C_OldProperty_x003E_k__BackingField>
</OldClass2>
当使用带有自定义绑定器的 NetDataContractSerializer 反序列化第一个示例来更改类型,然后在该类型上实现 ISerialized 或提供代理选择器来指定基本上履行 ISerializalbe 角色的序列化代理时,您将在 ISerializationSurrogate 中看到一个空的 SerializationInfo .SetObjectData 方法。当处理第二个示例中的 xml 时,SerializationInfo 似乎获得了正确的信息,并且一切按预期工作。
我的结论是,由于缺乏类型信息,NetDataContractSerializer 为仅通过 SerializedAttribute 支持序列化的类型生成的默认 XML 将与使用 ISerialized 或序列化代理技术的反序列化不兼容。因此,为了使 NetDataContractSerialized 的使用更加面向未来,应该自定义序列化以确保此类型信息包含在 XML 中,以便可以自定义以后的反序列化,而无需手动转换源 XML。