JSON易读通用但性能差、无校验;Protobuf高性能强契约但需.proto生成代码;MessagePack兼顾二进制效率与零schema灵活性;选型取决于变更成本与协作场景。
JSON 是最易读、最通用的序列化格式,std::string 就能承载,配合 nlohmann/json 库几行就能完成序列化/反序列化。但它本质是文本格式,解析慢、体积大、无 schema 约束。
json::parse() 在千条消息量级下可能比 Protobuf 慢 5–10 倍42 → "42"),浮点精度可能丢失(尤其 double)"id"、"name"),网络传输或磁盘存储时明显膨胀
匹配只能到运行时报错,比如把 "status" 写成 "stauts"
适用场景:配置文件、HTTP API 响应、日志输出、前端联调接口。
Protobuf 不是“选一个库就行”,它依赖代码生成:你写 .proto 文件,用 protoc 编译出 C++ 类,再调用 SerializeToString() 或 ParseFromString()。这意味着所有字段名、类型、嵌套关系在编译期就固定了。
ParseFromString() 几乎是 memcpy 级别开销reserved
.proto 文件、每次改结构要重新生成代码、不支持动态字段(如 mapgoogle.protobuf.Any)典型使用流程:
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
repeated string tags = 3;
}然后执行 protoc --cpp_out=. user.proto,生成 user.pb.h 和 user.pb.cc。
MessagePack 语义上接近 JSON(支持 map、array、string、int、float、bool、null),但用二进制编码,不带字段名冗余。C++ 用 msgpack-c 库,通过宏或自定义 MSGPACK_DEFINE 注册结构体成员即可序列化。
struct 改动小(加个宏就行)name → full_name 就断兼容)msgpack::object 可用于弱类型场景(如配置解析),但失去编译期类型安全示例结构体注册:
struct User {
int id;
std::string name;
MSGPACK_DEFINE(id, name);
};之后可直接用 msgpack::pack() 和 msgpack::unpack()。
如果通信双方都是你控制的 C++ 服务,且协议长期稳定,Protobuf 的编译期检查和极致性能值得前期投入;如果要和 Python/JS 快速对接又不愿写 IDL,MessagePack 是折中选择;如果只是临时导出数据给运维看、或者做 HTTP 调试,JSON 仍是最快路径。
最容易被忽略的是演进成本:Protobuf 的 tag 号一旦发布就不能改,MessagePack 字段名改了旧数据就无法识别,而 JSON 即使字段名错了也常靠默认值兜底——这看似灵活,实则把问题拖到线上才暴露。