You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:15:34