使用BinaryFormatter序列化反序列化时偶现无效二进制格式错误求助
问题分析与解决方案
你遇到的「输入流不是有效二进制格式」错误,本质是传入GetObject的byte[]并非BinaryFormatter序列化产生的合法数据,可能由以下几种原因导致,对应解决方案如下:
1. 无效的输入字节数组
如果GetBytes序列化失败返回null,而调用方未做判断直接将null或空数组传入GetObject,就会导致反序列化时流内容无效。
修复方案:在GetObject开头添加输入合法性校验:
public static TableData GetObject(byte[] arrBytes) { try { // 先校验输入是否有效 if (arrBytes == null || arrBytes.Length == 0) { Debug.Log("TableDataObject translater: Invalid empty or null byte array"); return null; } using (var memStream = new MemoryStream()) { var binForm = new BinaryFormatter(); memStream.Write(arrBytes, 0, arrBytes.Length); memStream.Seek(0, SeekOrigin.Begin); return (TableData)binForm.Deserialize(memStream); } } catch (Exception ex) { Debug.Log("TableDataObject translater: "+ ex.Message); return null; } }
同时在GetBytes中提前校验输入对象是否为null:
public static byte[] GetBytes(TableData obj) { try { if (obj == null) { Debug.Log("Byte maker: Cannot serialize null TableData object"); return null; } BinaryFormatter bf = new BinaryFormatter(); using (var ms = new MemoryStream()) { bf.Serialize(ms, obj); return ms.ToArray(); // using块会自动释放流,无需手动Close } } catch (Exception ex) { Debug.Log("Byte maker: "+ ex.Message); return null; } }
2. TableData类或嵌套类未标记可序列化特性
BinaryFormatter要求序列化的类及其所有嵌套引用类型成员都必须标记[Serializable]特性,否则序列化过程会生成不完整的字节数组,导致反序列化时格式错误。
修复方案:检查并确保相关类都添加特性:
[Serializable] public class TableData { // 成员变量如果是自定义类,也要标记[Serializable] public NestedData Data; } [Serializable] public class NestedData { // ... }
3. 数据存储/传输过程中损坏
如果字节数组是从文件、网络或本地存储读取的,可能存在数据截断、编码错误或篡改(比如写入文件时未完成IO操作就中断,网络传输丢包)。
修复方案:
- 序列化后记录字节数组的长度,反序列化前验证长度是否匹配;
- 添加简单的校验和(如CRC32)验证数据完整性,示例:
(注:Crc32实现可自行封装或使用第三方库)// 序列化时附加校验和 public static byte[] GetBytesWithChecksum(TableData obj) { var rawBytes = GetBytes(obj); if (rawBytes == null) return null; using (var ms = new MemoryStream()) { ms.Write(rawBytes, 0, rawBytes.Length); var crc = BitConverter.GetBytes(Crc32.Compute(rawBytes)); ms.Write(crc, 0, crc.Length); return ms.ToArray(); } } // 反序列化前校验 public static TableData GetObjectWithChecksum(byte[] arrBytes) { if (arrBytes == null || arrBytes.Length < 4) return null; var rawLength = arrBytes.Length - 4; var rawBytes = new byte[rawLength]; Array.Copy(arrBytes, 0, rawBytes, 0, rawLength); var expectedCrc = BitConverter.ToUInt32(arrBytes, rawLength); if (Crc32.Compute(rawBytes) != expectedCrc) { Debug.Log("TableDataObject translater: Data corrupted"); return null; } return GetObject(rawBytes); }
4. 替换过时的BinaryFormatter(推荐)
BinaryFormatter已被官方标记为过时,存在安全风险(反序列化恶意数据可能导致代码执行),且兼容性差。建议替换为更可靠的序列化库:
示例:使用System.Text.Json
using System.Text.Json; public static byte[] GetBytes(TableData obj) { try { if (obj == null) return null; return JsonSerializer.SerializeToUtf8Bytes(obj); } catch (Exception ex) { Debug.Log("Byte maker: "+ ex.Message); return null; } } public static TableData GetObject(byte[] arrBytes) { try { if (arrBytes == null || arrBytes.Length == 0) return null; return JsonSerializer.Deserialize<TableData>(arrBytes); } catch (Exception ex) { Debug.Log("TableDataObject translater: "+ ex.Message); return null; } }
示例:使用Protobuf-net(高性能二进制序列化)
先安装NuGet包protobuf-net,然后给类添加特性:
using ProtoBuf; [ProtoContract] public class TableData { [ProtoMember(1)] public int Id { get; set; } [ProtoMember(2)] public string Name { get; set; } } // 序列化/反序列化方法 public static byte[] GetBytes(TableData obj) { try { if (obj == null) return null; using (var ms = new MemoryStream()) { Serializer.Serialize(ms, obj); return ms.ToArray(); } } catch (Exception ex) { Debug.Log("Byte maker: "+ ex.Message); return null; } } public static TableData GetObject(byte[] arrBytes) { try { if (arrBytes == null || arrBytes.Length == 0) return null; using (var ms = new MemoryStream(arrBytes)) { return Serializer.Deserialize<TableData>(ms); } } catch (Exception ex) { Debug.Log("TableDataObject translater: "+ ex.Message); return null; } }
内容的提问来源于stack exchange,提问作者Szendeffy Bálint
相关产品推荐
相关产品推荐

