如何序列化Delegate类型属性?DataContractSerializer序列化报错求助
尝试用DataContractSerializer将Delegate序列化到XML文件时,抛出第一个异常:
System.Runtime.Serialization.SerializationException : Type 'System.DelegateSerializationHolder+DelegateEntry' with data contract name 'DelegateSerializationHolder.DelegateEntry:http://schemas.datacontract.org/2004/07/System' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.
编写自定义DataContractResolver后,又触发第二个异常:
System.ArgumentException : Invalid name character in '<>c'. The '<' character, hexadecimal value 0x3C, cannot be included in a name.
自定义Resolver代码如下:
public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) { var name = type.Name; var nameSpace = type.Namespace; typeName = new XmlDictionaryString(XmlDictionary.Empty, name, 0); if (nameSpace != null) { typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, nameSpace, 0); if (!dictionary.ContainsKey(type.Name)) dictionary.Add(name, typeName); if (type.Namespace != null && !dictionary.ContainsKey(type.Namespace)) dictionary.Add(nameSpace, typeNamespace); return true; } typeName = null; typeNamespace = null; return false; }
疑问:
- 自定义Resolver哪里出错了?怎么修复?
- 还有其他序列化Delegate的方法吗?
自定义Resolver的问题分析
- 非法字符报错原因:你直接使用
type.Name作为XML元素名,但Delegate关联的闭包类型(编译器自动生成的<>c这类名称)包含<>等XML命名禁止的特殊字符,违反XML规范。 - 逻辑缺陷:Resolver没有针对性处理Delegate相关的系统内部类型(比如
DelegateSerializationHolder.DelegateEntry),反而对所有类型返回自定义名称,扩大了问题范围。
修复后的Delegate专用Resolver
public class DelegateDataContractResolver : DataContractResolver { private readonly Dictionary<string, Type> _typeMap = new Dictionary<string, Type>(); private readonly XmlDictionary _xmlDict = new XmlDictionary(); public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) { // 只处理Delegate类型或系统内部的Delegate包装类型 if (typeof(Delegate).IsAssignableFrom(type) || type.FullName == "System.DelegateSerializationHolder+DelegateEntry") { // 清理XML非法字符,替换为下划线 string safeTypeName = Regex.Replace(type.Name, @"[^\w\-.]", "_"); string safeNamespace = type.Namespace ?? "http://schemas.datacontract.org/2004/07/System"; typeName = _xmlDict.Add(safeTypeName); typeNamespace = _xmlDict.Add(safeNamespace); if (!_typeMap.ContainsKey(safeTypeName)) _typeMap.Add(safeTypeName, type); return true; } // 其他类型交给默认解析器处理 return knownTypeResolver.TryResolveType(type, declaredType, knownTypeResolver, out typeName, out typeNamespace); } public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver) { // 反序列化时根据映射找回类型 if (_typeMap.TryGetValue(typeName, out Type targetType)) return targetType; return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, knownTypeResolver); } }
使用方式
初始化序列化器时传入该Resolver:
var serializer = new DataContractSerializer( typeof(YourContainingType), null, int.MaxValue, false, true, null, new DelegateDataContractResolver() );
其他序列化Delegate的方案
- BinaryFormatter:对Delegate序列化支持完善,直接调用即可,但官方标记为过时,仅建议在可信环境使用。
- 手动序列化关键信息:不直接序列化Delegate实例,而是序列化其目标类型、方法名,反序列化时重建Delegate:
// 序列化逻辑 public void WriteDelegate(XmlWriter writer, Delegate del) { writer.WriteElementString("TargetType", del.Target?.GetType().AssemblyQualifiedName); writer.WriteElementString("MethodName", del.Method.Name); } // 反序列化逻辑 public Delegate ReadDelegate(XmlReader reader, Type delegateType) { string targetTypeStr = reader.ReadElementString("TargetType"); string methodName = reader.ReadElementString("MethodName"); Type targetType = Type.GetType(targetTypeStr); MethodInfo method = targetType.GetMethod(methodName); return Delegate.CreateDelegate(delegateType, null, method); } - Newtonsoft.Json:JSON格式下对Delegate序列化支持较好,默认即可处理,或手动序列化方法元数据。
内容的提问来源于stack exchange,提问作者Learner

