BinaryFormatter与SoapFormatter类型处理差异及自定义字典序列化问题
问题分析与解决办法
问题根源
你遇到的问题核心在于 SoapFormatter 不会自动为声明为 object 类型的值序列化具体类型元数据,而 BinaryFormatter 会默认包含这些信息。当你把 Address 或 Being 实例以 object 类型存入字典时,SoapFormatter 生成的 SOAP 数据里没有记录这些对象的实际类型。反序列化时,它无法判断该创建哪种类型的实例,导致非字符串类型的对象变成 null(字符串属于原生类型,SoapFormatter 能自动识别)。
解决办法
方法一:使用序列化代理(Surrogate)统一处理 object 类型
创建一个序列化代理,专门为 object 类型的值附加类型信息,序列化时存入类型名,反序列化时根据类型名实例化正确对象。
代码示例:
// 定义序列化代理 public class ObjectSurrogate : ISerializationSurrogate { public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { // 存入对象的完整类型名 info.AddValue("Type", obj.GetType().AssemblyQualifiedName); // 存入对象数据 info.AddValue("Data", obj); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { // 读取类型名并获取类型 var typeName = info.GetString("Type"); var targetType = Type.GetType(typeName); // 根据类型反序列化对象 return info.GetValue("Data", targetType); } }
使用时给 SoapFormatter 注册这个代理:
IFormatter formatter = new SoapFormatter(); var surrogateSelector = new SurrogateSelector(); // 为 object 类型注册代理 surrogateSelector.AddSurrogate(typeof(object), new StreamingContext(StreamingContextStates.All), new ObjectSurrogate()); formatter.SurrogateSelector = surrogateSelector; // 后续的序列化/反序列化代码保持不变 FileStream s = new FileStream("test.data", FileMode.Create); formatter.Serialize(s, input); s.Close(); FileStream s2 = new FileStream("test.data", FileMode.Open); var output = (CustomDictionary)formatter.Deserialize(s2); s2.Close();
方法二:在自定义字典中手动实现 ISerializable
不依赖基类 Dictionary 的序列化逻辑,自己控制每个字典条目的序列化过程,显式存储每个值的类型信息。
修改后的 CustomDictionary 代码:
[Serializable] public class CustomDictionary : Dictionary<int, object>, ISerializable { public CustomDictionary() { } // 反序列化构造函数 protected CustomDictionary(SerializationInfo info, StreamingContext context) : base() { // 读取保存的条目列表 var entries = (List<DictionaryEntryWithType>)info.GetValue("Entries", typeof(List<DictionaryEntryWithType>)); foreach (var entry in entries) { var targetType = Type.GetType(entry.TypeName); this.Add(entry.Key, entry.Value); } } // 手动实现序列化逻辑 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { // 将每个条目转换为包含类型信息的对象并保存 var entries = this.Select(kvp => new DictionaryEntryWithType(kvp.Key, kvp.Value)).ToList(); info.AddValue("Entries", entries); } // 辅助类:存储带类型信息的字典条目 [Serializable] private class DictionaryEntryWithType { public int Key { get; set; } public string TypeName { get; set; } public object Value { get; set; } // 序列化用构造函数 public DictionaryEntryWithType(int key, object value) { Key = key; TypeName = value.GetType().AssemblyQualifiedName; Value = value; } // 反序列化需要无参构造函数 public DictionaryEntryWithType() { } } }
这样修改后,SoapFormatter 就能正确序列化和反序列化所有类型的对象了。
内容的提问来源于stack exchange,提问作者ExpFRML
相关产品推荐
相关产品推荐

