我有一种情况,我正在使用NetDataContractSerializer序列化一些。net对象,并将XML存储在数据库中,作为在应用程序中记住这些对象状态的一种方式。最近我遇到了第一个情况,一些属性和类型名称的代码重构导致无法反序列化此XML数据。
到目前为止,我已经提出了两种不同的攻击计划,如何处理版本兼容性中断,例如使用NetDataContractSerializer本身中可用的工具来控制反序列化或直接转换XML。从我的实验和研究来看,似乎可以使用自定义SerializationBinder反序列化为不同的类型,并且可以通过实现isserializable或通过实现ISurrogateSelector和isserializationsurrogate编写序列化代理来解决属性名称/类型更改。不幸的是,这种首选机制并没有成功,除非我能证明,否则使用NetDataContractSerializer在序列化数据的不同版本之间移动似乎是不可能的,这是由于Microsoft的一些无法解释的设计决策。Microsoft所建议的是在两边使用相同的序列化,这完全违背了使用代理来帮助类型名称更改或移动到不同的名称空间或程序集的目的。要修复它,请使用相同的NetDataContractSerializer实例或对象初始化的另一个实例SurrogateSelector .
这个解释与MSDN的一篇文章相冲突,这篇文章是关于使用自定义绑定来替换类型以及处理序列化结构中的其他更改的。
在反序列化期间,格式化程序看到已设置了绑定器。当每个对象即将被反序列化时,格式化程序调用绑定器的BindToType方法,将程序集名称和类型传递给它格式化程序想要反序列化。此时,由BindToType决定应该实际构造什么类型并返回该类型。
注意,原始类型和新类型必须具有完全相同的字段方法使用简单序列化的新类型的名称和类型可序列化的自定义属性。但是,新版本的类型可以实现isserializable接口,然后它的特殊构造函数将被调用,并且该类型可以检查SerializationInfo对象,并确定如何反序列化自身。
因此,要么我将能够让NetDataContractSerializer工作,将我的V1 XML反序列化为我的V2类型,要么我将不得不手动转换XML。如果有人能证明NetDataContractSerializer的SerializationInfo在使用isserializable或使用序列化代理时确实有效,那将是非常好的,或者至少给出一个比微软给出的更好的解释,否则我可能会发布一个新的问题来讨论。net中直接转换旧XML的最佳方法。
更新2011-08-16:经过一些实验后,如果被序列化的原始类型实现了isserializable,那么isserializable和序列化代理技术似乎都工作得很好,否则,如果该类型只是使用了[Serializable]属性,那么对象图中的每个字段似乎都以额外属性的形式缺少一些有价值的类型信息。
使用[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和自定义绑定器对第一个示例进行反序列化时,更改类型,然后在该类型上实现isserializable,或者提供指定序列化代理的代理选择器,该代理基本上完成了isserializalbe角色,然后您将在isserializationsurrogate中看到一个空SerializationInfo。SetObjectData方法。在第二个示例中处理xml时,SerializationInfo似乎获得了正确的信息,并且一切都如预期的那样工作。
我的结论是,由于缺乏类型信息,NetDataContractSerializer为仅通过SerializableAttribute支持序列化的类型生成的默认XML将与使用isserializable或序列化代理技术的反序列化不兼容。因此,为了使NetDataContractSerializable的使用更具前瞻性,应该自定义序列化,以确保该类型信息包含在XML中,以便以后的反序列化可以自定义,而无需手动转换源XML。
是的,如果使用序列化,您必须有一个经过深思熟虑的数据迁移路径。您提到的解决方案是我个人会做的,在代码中包含反序列化的检查。如果检测到较旧的版本,则进行小的转换,使其与新格式匹配,并根据需要继续进行。一旦所有的数据都被转换,这些代码就可以在将来的版本中被废弃。