messagepack 默认仅支持 public 字段/属性,需显式契约或 contractless 模式;序列化结果为二进制,非 utf-8 文本,误用字符串处理会导致“乱码”;自定义类须标注 [messagepackobject] 并配 [key(n)] 或启用 contractlessstandardresolver(禁用于生产);私有字段、只读属性、继承等需额外配置 resolver 或属性标记;务必显式传入 options 避免线程安全问题;[key] 序号不可变更以保障向后兼容。

直接用 MessagePackSerializer.Serialize 和 MessagePackSerializer.Deserialize 是最简路径,但不加约束会抛 NotSupportedException: Type xxx is not supported —— 默认只认 public 字段/属性,且要求类型有显式契约或启用 contractless 模式。
为什么序列化后是乱码?
因为 MessagePack 输出的是原始二进制字节,不是 UTF-8 文本。用 File.WriteAllText 或 Encoding.UTF8.GetString(bytes) 处理,等于把字节当字符串硬解,必然“乱码”甚至反序列化失败。
- 写入文件必须用
File.WriteAllBytes(path, bytes)或FileStream.Write - 调试时想“看内容”,可用
Convert.ToBase64String(bytes)临时转成可读字符串(仅限调试) - 别在日志里直接
.ToString()打印字节数组——它输出的是类型名,不是内容
自定义 class 怎么才能被序列化?
默认不支持未标注的普通 class。必须满足以下任一条件:
- 加
[MessagePackObject]+ 每个字段/属性加[Key(n)](n 从 0 开始,不可变) - 或用
[MessagePackObject(keyAsPropertyName: true)],此时字段名自动作键,但需配合[IgnoreMember]排除不需要的成员 - 或启用
ContractlessStandardResolver:仅限原型开发,生产环境禁用——每次反射推导结构,性能暴跌
示例:
[MessagePackObject]
public class User
{
[Key(0)] public string Name { get; set; }
[Key(1)] public int Age { get; set; }
}
私有字段、只读属性、继承怎么处理?
这些都不是默认支持的,强行序列化会静默跳过或报错。
- 私有字段:需传
StandardResolverAllowPrivate.Options作为options参数,且类仍需[MessagePackObject] - 只读属性(如
public string Id { get; }):必须配[SerializationConstructor]指定构造函数,否则反序列化失败 - 继承结构:基类和子类都得标
[MessagePackObject],并用[Union]显式声明类型映射,否则子类信息丢失 - 枚举默认转整数;要字符串形式,得加
[EnumMember(Value = "xxx")]并设EnumAsString = true
Options 不传行不行?
能跑,但危险。默认走 MessagePackSerializerOptions.Default,它是静态全局单例,多线程下可能被意外覆盖,引发不可复现的反序列化错误。
- 始终显式传
MessagePackSerializerOptions.Standard或自定义实例 - 若需兼容旧版 schema,用
MessagePackSerializerOptions.Standard.WithCompatibilityResolver(),但注意体积会增大(写全路径类型名) - 首次序列化某类型最慢,后续缓存;contractless 模式缓存效果差,别在高频小对象场景用
真正容易被忽略的,是 [Key] 序号一旦发布就不能改——哪怕只是重排字段顺序,也会导致老数据无法反序列化。版本演进必须靠 [IgnoreMember] 或 [SkipIfDefault] 控制字段生命周期,而不是碰运气。











