C# MessagePack序列化文件 C#如何使用MessagePack进行快速二进制序列化

2次阅读

messagepackserializer.serialize 输出乱码是因为它是二进制格式,须用file.writeallbytes或stream写入原始字节,不可用字符串方法处理;自定义类需加[messagepackObject]和[key(n)]标记才能序列化。

C# MessagePack序列化文件 C#如何使用MessagePack进行快速二进制序列化

MessagePackSerializer.Serialize 为什么序列化出来全是乱码?

因为 MessagePack 是二进制格式,不是文本。直接用 File.WriteAllText 或写入字符串流会破坏字节,导致“乱码”或反序列化失败。

  • 必须用 File.WriteAllBytes 写入原始字节,或用 Stream(如 FileStream)配合 MessagePackSerializer.Serialize
  • 别试图用 Encoding.UTF8.GetString(bytes) 去“看内容”——这不是设计来给人读的
  • 调试时想确认是否正常,可用 Convert.ToBase64String(bytes) 短暂转成可读字符串,但仅限调试,不可用于存储或传输

如何让自定义类支持 MessagePack 序列化?

默认情况下,MessagePackSerializer.Serialize 无法处理未标记的普通 class,会抛出 NotSupportedException: Type xxx is not supported

  • 最简单方式:给类加 [MessagePackObject],每个要序列化的字段/属性加 [Key(n)](n 为整数 ID,建议从 0 开始连续)
  • 不加 [Key] 也能工作,但需启用契约生成器(见下一点),否则仍失败
  • 注意字段访问修饰符:private 字段默认不被序列化,除非显式加 [SerializationConstructor] 或开启 ContractlessStandardResolver.Options
[MessagePackObject] public class User {     [Key(0)] public string Name { get; set; }     [Key(1)] public int Age { get; set; } }

使用 ContractlessStandardResolver 后性能反而变差?

是的,开合约无关模式(contractless)会让 MessagePack 放弃预生成序列化器,每次调用都反射推导结构,显著拖慢速度,尤其在高频小对象场景。

  • 仅在原型开发或类型极动态(如 ExpandoObject)时临时启用:MessagePackSerializerOptions.Standard.WithResolver(ContractlessStandardResolver.Instance)
  • 生产环境务必用带 [MessagePackObject] 的显式契约,并配合 MessagePackSerializer.GetFormatter<t>()</t> 预热
  • 首次序列化某个类型时最慢,后续会缓存;但 contractless 模式下缓存粒度更粗、命中率更低

文件序列化后体积比 json 还大?

通常不会,MessagePack 二进制格式本应更紧凑。体积异常往往源于没关掉兼容性选项或用了低效配置。

  • 检查是否误启用了 CompatibilityOptions(如 UseSimpleAssemblyName = false),这会让类型名全路径写入,大幅膨胀
  • 避免序列化含大量空值或默认值的字段——MessagePack 不自动跳过,需手动用 [IgnoreDataMember][SkipIfDefault]
  • 数组和字典默认不压缩重复键,如果键固定(如日志事件中的 "timestamp", "level"),考虑用 Dictionary<byte object></byte> 或预定义 schema 替代 string key

MessagePack 的快,依赖的是确定性结构和预编译契约。一旦退回到运行时反射或冗余元数据,优势就消失了。很多人卡在第一步——把 byte[] 当字符串处理,后面再怎么调参都白搭。

text=ZqhQzanResources