MessagePack 编解码
把 JSON 编成 MessagePack 的十六进制和 Base64,或者把抓到的二进制解回 JSON,两种方向来回都对得上。
编码规则
字符串按长度分 fixstr、str8、str16、str32,数组和 map 按元素个数分 fixarray、array16、array32,整数按大小选 1、2、4、8 字节。小整数直接塞进类型字节里,所以短的键值对能压到每个字段一两个字节。
浮点数统一用 float64,保证和 JSON 的 number 精度一致;一字节和两字节的 float32 只在解码时出现。负数用补码,-32 到 -1 直接放在类型字节里,不额外占空间。
JSON 之外的类型用带 $ 的约定表示:{"$bin":"…"} 是二进制,{"$ext":类型,"data":"…"} 是扩展类型,{"$u64":"…"} 和 {"$i64":"…"} 是超出安全范围的 64 位整数。这套约定只在页面里用,不是 MessagePack 规范的一部分。
想换一种序列化格式做对比,用CBOR 编解码或Protobuf 解码。
常见问题
MessagePack 比 JSON 小多少?
常见数据能小 20% 到 50%,字段名短、数字多、结构重复时差距更明显。整数按 1、2、4、8 字节分段编码,字符串只加一两个字节的长度前缀,不像 JSON 那样到处是引号和冒号。但可读性换来的是完全没法直接看,调试时还是要转回 JSON。
为什么整数和浮点数的结果不一样?
JSON 里 1 和 1.0 在解析后都是 number,编码时按是否为整数决定用整数段还是 float64。所以 1.0 会被编成整数 1,解回来也是 1。要保留浮点语义,得在业务层自己约定,或者在 JSON 里写成字符串。
二进制数据怎么表示?
JavaScript 的 JSON 没有字节串类型,这里用 {"$bin":"0a0b"} 表示,值是一段十六进制。解码时遇到 bin8/bin16/bin32 也会输出成这个形式,编码时再原样还原,来回不会丢字节。
时间戳扩展是什么?
MessagePack 的 ext type -1 专门表示时间,有 4 字节秒、8 字节秒加纳秒、12 字节纳秒加秒三种长度。解码时直接转成 ISO 8601 字符串,这样在 JSON 里能读;其它 ext 类型会原样输出成 {"$ext":类型,"data":"..."}。
为什么超出安全整数的数变成了字符串?
JavaScript 的 number 只能精确表示到 2^53-1,再大就会丢精度。64 位整数超出这个范围时,解码结果里用 {"$u64":"..."} 或 {"$i64":"..."} 保留原值,避免看起来正常但其实已经算错。
末尾多出来的字节会被忽略吗?
不会静默丢掉,解析提示里会写明只消费了多少字节、还剩多少。多出来的部分通常是粘错了数据、或者流式传输时截取了半条消息,看到提示就该检查数据源。