无xsi:type时DataContractResolver未触发,旧DataContract XML反序列化兼容问题
解决方案
你可以通过以下三种更稳定的方案实现兼容反序列化,无需做字符串硬替换:
方案1:为派生类指定与旧类型匹配的协定名称
直接给NewType的[DataContract]特性显式声明和旧OldType完全一致的名称和命名空间,同时将NewType添加到序列化器的已知类型列表中:
// 给NewType指定旧类型的协定名和命名空间 [DataContract(Name = "OldType", Namespace = "你的旧OldType对应的命名空间")] class NewType : AbstractType { // 仅包含非序列化字段 }
初始化序列化器时传入已知类型:
var serializer = new DataContractSerializer( typeof(List<AbstractType>), new List<Type> { typeof(NewType) } // 注册已知派生类型 );
这个方案适配成本最低,适合新旧类型字段结构完全匹配的场景。
方案2:使用IDataContractSurrogate做类型映射
如果需要更灵活的类型转换逻辑,可以实现IDataContractSurrogate接口,在反序列化阶段将旧类型直接映射到NewType实例:
public class TypeMigrationSurrogate : IDataContractSurrogate { public Type GetDataContractType(Type type) { // 序列化阶段的映射逻辑按需实现 return type; } public object GetDeserializedObject(object obj, Type targetType) { // 反序列化时如果拿到旧类型实例,直接转换为NewType if (obj is OldType oldObj) { return new NewType { Hello = oldObj.Hello }; } return obj; } // 其余接口方法直接返回默认值即可,不需要额外实现 public object GetObjectToSerialize(object obj, Type targetType) => obj; public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => null; public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) {} public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) => null; public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) => typeDeclaration; }
初始化序列化器时传入代理:
var serializer = new DataContractSerializer( typeof(List<AbstractType>), new DataContractSerializerSettings { DataContractSurrogate = new TypeMigrationSurrogate(), KnownTypes = new List<Type> { typeof(OldType), typeof(NewType) } });
方案3:流式处理XML节点(比字符串替换更安全)
如果不想修改类型的特性定义,可以在反序列化前用XmlReader包装原始输入,动态修改节点名、添加需要的xsi:type属性,全程在内存流中处理,不会有字符串替换的逃逸问题:
public static XmlReader CreateMigratingXmlReader(Stream inputStream) { var reader = XmlReader.Create(inputStream); // 自定义XmlReader的实现,遇到<OldType>节点时替换为<AbstractType>,同时添加xsi:type="b:NewType"属性和对应命名空间声明 // 也可以用XmlWrappingReader之类的现成封装简化实现 }
注意:DataContractResolver本身的设计就是仅处理带
xsi:type的多态类型节点,没有全局触发的配置项,不适合这个场景使用。
内容的提问来源于stack exchange,提问作者martinyyyy
相关产品推荐
相关产品推荐

