如何用其他序列化器反序列化BinaryFormatter序列化的ConcurrentDictionary?
解决方案:从BinaryFormatter迁移到安全序列化器
核心思路
你之前用BinaryFormatter生成的文件是.NET专属的二进制序列化格式,和JSON、XML、Protobuf等格式完全不兼容,直接用其他序列化器读取必然失败。正确做法是临时用BinaryFormatter读取现有数据,转存为安全序列化器支持的格式,后续业务全程使用新的序列化器读写。
虽然BinaryFormatter存在安全风险,但仅用于读取你自身生成的可信文件时,临时使用是安全的,完成迁移后即可彻底弃用它。
步骤1:迁移现有数据
先编写代码,用BinaryFormatter读取旧文件拿到数据,再用安全序列化器(以Newtonsoft.Json为例)写入新文件:
using System.Collections.Concurrent; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using Newtonsoft.Json; // 原BinaryFormatter生成的旧文件路径 const string OldBinaryFilePath = "your_old_file.bin"; // 新的JSON格式文件路径 const string NewJsonFilePath = "jobs_ack.json"; // 1. 临时用BinaryFormatter读取旧数据 ConcurrentDictionary<string, DateTime> jobsAck; var binaryFormatter = new BinaryFormatter(); using (var stream = File.Open(OldBinaryFilePath, FileMode.Open)) { jobsAck = (ConcurrentDictionary<string, DateTime>)binaryFormatter.Deserialize(stream); } // 2. 用Newtonsoft.Json序列化到新文件 var jsonSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, // 可选,格式化输出便于查看 DateTimeZoneHandling = DateTimeZoneHandling.Utc // 统一时区避免解析异常 }; File.WriteAllText(NewJsonFilePath, JsonConvert.SerializeObject(jobsAck, jsonSettings));
步骤2:后续业务的读写逻辑
迁移完成后,后续创建任务和应用启动加载数据都用Newtonsoft.Json处理:
序列化(创建任务时)
using System.Collections.Concurrent; using System.IO; using Newtonsoft.Json; ConcurrentDictionary<string, DateTime> _jobsAck = new ConcurrentDictionary<string, DateTime>(); // 业务逻辑:向_jobsAck添加任务数据... const string JsonFilePath = "jobs_ack.json"; var jsonSettings = new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Utc }; File.WriteAllText(JsonFilePath, JsonConvert.SerializeObject(_jobsAck, jsonSettings));
反序列化(应用启动时)
using System.Collections.Concurrent; using System.IO; using Newtonsoft.Json; const string JsonFilePath = "jobs_ack.json"; ConcurrentDictionary<string, DateTime> _jobsAck; if (File.Exists(JsonFilePath)) { var jsonContent = File.ReadAllText(JsonFilePath); _jobsAck = JsonConvert.DeserializeObject<ConcurrentDictionary<string, DateTime>>(jsonContent); } else { _jobsAck = new ConcurrentDictionary<string, DateTime>(); }
可选方案:使用Protobuf-Net
如果偏好二进制格式(体积更小、序列化速度更快),可以用protobuf-net。先安装NuGet包protobuf-net,再按以下代码操作:
迁移代码
using System.Collections.Concurrent; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using ProtoBuf; const string OldBinaryFilePath = "your_old_file.bin"; const string NewProtobufFilePath = "jobs_ack.pb"; // 读取旧数据 ConcurrentDictionary<string, DateTime> jobsAck; var binaryFormatter = new BinaryFormatter(); using (var stream = File.Open(OldBinaryFilePath, FileMode.Open)) { jobsAck = (ConcurrentDictionary<string, DateTime>)binaryFormatter.Deserialize(stream); } // 用Protobuf序列化 using (var stream = File.Open(NewProtobufFilePath, FileMode.Create)) { Serializer.Serialize(stream, jobsAck); }
后续读写
序列化
using System.Collections.Concurrent; using System.IO; using ProtoBuf; ConcurrentDictionary<string, DateTime> _jobsAck = new ConcurrentDictionary<string, DateTime>(); // 业务逻辑:添加任务数据... const string ProtobufFilePath = "jobs_ack.pb"; using (var stream = File.Open(ProtobufFilePath, FileMode.Create)) { Serializer.Serialize(stream, _jobsAck); }
反序列化
using System.Collections.Concurrent; using System.IO; using ProtoBuf; const string ProtobufFilePath = "jobs_ack.pb"; ConcurrentDictionary<string, DateTime> _jobsAck; if (File.Exists(ProtobufFilePath)) { using (var stream = File.Open(ProtobufFilePath, FileMode.Open)) { _jobsAck = Serializer.Deserialize<ConcurrentDictionary<string, DateTime>>(stream); } } else { _jobsAck = new ConcurrentDictionary<string, DateTime>(); }
关键说明
- 之前直接用XML/JSON/Protobuf读取失败的原因:每个序列化器都有专属格式规范,BinaryFormatter的二进制格式包含.NET类型元数据,和其他序列化器的结构完全不匹配,无法跨格式直接解析。
- 迁移完成后建议删除旧的BinaryFormatter生成文件,避免后续误操作。
- 序列化器选择:JSON可读性强、调试方便;Protobuf体积小、性能更高,可根据业务需求自行选择。
内容的提问来源于stack exchange,提问作者Vivek Nuna
相关产品推荐
相关产品推荐

