5

我有一种情况,我正在使用 NetDataContractSerializer 序列化一些 .NET 对象,并将 XML 存储在数据库中,以此来记住应用程序中这些对象的状态。最近我刚刚遇到第一种情况,其中一些属性和类型名称的代码重构导致无法反序列化此 XML 数据。

到目前为止,我已经提出了两种不同的攻击计划,用于处理版本兼容性中断,例如使用 NetDataContractSerializer 本身提供的工具来控制反序列化或直接转换 XML。根据我的实验和研究,似乎可以使用自定义 SerializationBinder反序列化为不同的类型和属性名称/类型更改可以通过实现 ISerializable 或通过实现 ISurrogateSelector 和 ISerializationSurrogate 编写序列化代理来解决。不幸的是,这种首选机制还没有被淘汰,除非我可以显示,否则它似乎使用代理在序列化数据的版本之间移动是不可能的,这是由于Microsoft 一些无法解释的设计决定。微软的建议是在双方都使用相同的序列化,这完全违背了使用代理来帮助在类型名称更改或移动到不同的命名空间或程序集的情况下的目的。

要修复它,请使用相同的 NetDataContractSerializer 实例或也使用兼容的 SurrogateSelector 初始化的另一个实例。

这种解释与MSDN 文章相冲突,该文章说使用自定义绑定器替换类型以及处理序列化结构中的其他更改。

在反序列化期间,格式化程序会看到已设置了一个活页夹。由于每个对象都将被反序列化,格式化程序调用活页夹的 BindToType 方法,将格式化程序想要反序列化的程序集名称和类型传递给它。此时,BindToType 决定实际应该构造什么类型并返回该类型。

请注意,如果新类型通过 Serializable 自定义属性使用简单序列化,则原始类型和新类型必须具有完全相同的字段名称和类型。但是,该类型的新版本可以实现 ISerializable 接口,然后将调用其特殊构造函数,该类型可以检查 SerializationInfo 对象中的值并确定如何反序列化自身。

因此,要么我将能够让 NetDataContractSerializer 将我的 V1 XML 反序列化为我的 V2 类型,要么我将不得不手动转换 XML。如果有人可以证明 NetDataContractSerializer 的 SerializationInfo 在使用 ISerializable 或使用序列化代理时确实有效,这将是优秀的,或者至少比微软给出的解释更好,否则我可能会发布一个新问题来辩论最佳方式在 .NET 中直接转换旧的 XML。

更新 2011-08-16: 经过一些实验后,如果被序列化的原始类型实现 ISerializable,则 ISerializable 和序列化代理技术似乎都可以正常工作,否则如果该类型仅使用 [Serializable] 属性,则似乎对象中的每个字段graph 缺少一些额外属性形式的有价值的类型信息。

使用[Serializable]属性的示例

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

实现ISerialzable 的示例:

<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 来反序列化第一个示例以更改类型,然后在该类型上实现 ISerializable 或提供一个代理选择器来指定基本上满足 ISerializalbe 角色的序列化代理时,您将在 ISerializationSurrogate 中看到一个空的 SerializationInfo .SetObjectData 方法。在第二个示例中处理 xml 时,SerializationInfo 似乎获得了正确的信息,并且事情按预期工作。

我的结论是,NetDataContractSerializer 为仅通过 SerializableAttribute 支持序列化的类型生成的默认 XML 将与使用 ISerializable 或序列化代理技术的反序列化不兼容,因为缺少类型信息。因此,为了使 NetDataContractSerializable 的使用更具前瞻性,应该自定义序列化以确保 XML 中包含此类型信息,以便以后可以自定义反序列化,而无需手动转换源 XML。

4

1 回答 1

2

是的,如果您使用序列化,您必须有一个经过深思熟虑的数据迁移路径。您提到的解决方案是我个人会做的,包括检查将反序列化的代码。如果检测到旧版本,请进行小的转换,使其与新格式匹配,然后根据需要继续操作。一旦转换了所有数据,该代码就可以在未来的版本中被淘汰。

于 2011-08-16T13:24:57.300 回答