json2pb

qianmoQqianmoQ· 更新于 2026-10-05· 阅读 7 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

json2pb

学习如何将 json 转换为 pb。

brpc 支持 json 与 protobuf 之间的双向转换,实现位于 json2pb,json 解析使用 rapidjson。该功能对 pb 2.x 和 3.x 均有效。pb3 内置了 json 转换 的功能。

按照设计,通过 HTTP + json 访问 protobuf 服务是对外提供服务的常见方式,因此转换必须精准,转换规则列举如下。

message

对应 rapidjson 的 Object,用花括号包围,其中的元素会被递归地解析。

// protobuf
message Foo {
    required string field1 = 1;
    required int32 field2 = 2;
}
message Bar {
    required Foo foo = 1;
    optional bool flag = 2;
    required string name = 3;
}

// rapidjson
{"foo":{"field1":"hello", "field2":3},"name":"Tom" }

重复字段

对应 rapidjson 的 Array,以方括号包围,其中的元素会被递归地解析。与 message 不同,每个元素的类型相同。

// protobuf
repeated int32 numbers = 1;

// rapidjson
{"numbers" : [12, 17, 1, 24] }

特别地,对于仅包含一个 repeated 类型成员的 message,序列化为 json 时支持直接序列化为数组,以简化包体。

// protobuf
message Foo {
    required int32 numbers = 1;
}
// rapidjson
[12, 17, 1, 24]

该特性默认为关闭状态,客户端在发送请求时,或服务端在发送回复时,可手动开启:

brpc::Controller cntl;
cntl.set_pb_single_repeated_to_array(true);

map

满足如下条件的 repeated MSG 被视作 json map:

  • MSG 包含一个名为 key 的字段,类型为 string,tag 为 1。
  • MSG 包含一个名为 value 的字段,tag 为 2。
  • 不包含其他字段。

这种"map"的属性有:

  • 自然不能确保 key 有序或不重复,用户视需求自行检查。
  • 与 protobuf 3.x 中的 map 二进制兼容,故 3.x 中的 map 使用 pb2json 也会正确地转化为 json map。

如果符合所有条件的 repeated MSG 并不需要被认为是 json map,打破上面任一条件就行了:在 MSG 中加入 optional int32 this_message_is_not_map_entry = 3; 这个办法破坏了"不包含其他字段"这项,且不影响二进制兼容。也可以调换 key 和 value 的 tag 值,让前者为 2 后者为 1,也使条件不再满足。

integers

rapidjson 会根据值打上对应的类型标记,比如:

  • 对于 3,rapidjson 中的 IsUInt、IsInt、IsUint64、IsInt64 等函数均会返回 true。
  • 对于 -1,则 IsUInt 和 IsUint64 会返回 false。
  • 对于 5000000000,IsUInt 和 IsInt 是 false。

这使得我们不用特殊处理,转化代码就可以自动地把 json 中的 UInt 填入 protobuf 中的 int64,而不是机械地认为这两个类型不匹配。相应地,转化代码自然能识别 overflow 和 underflow,当出现时会转化失败。

// protobuf
int32 uint32 int64 uint64

// rapidjson
Int UInt Int64 UInt64

floating point

json 的整数类型也可以转至 pb 的浮点数类型。浮点数(IEEE754)除了普通数字外还接受 "NaN"、"Infinity"、"-Infinity" 三个字符串,分别对应 Not A Number、正无穷、负无穷。

// protobuf
float double

// rapidjson
Float Double Int Uint Int64 Uint64

enum

enum 可转换为整数,也可转换为其名字对应的字符串,具体由 Pb2JsonOptions.enum_options 控制,默认转换为后者。

string

默认同名转换。但当 JSON 中出现非法的 C++ 变量名(即 pb 的变量名规则)时,仍允许转换,规则为:

illegal-char <-> **_Z**<ASCII-of-the-char>**_**

bytes

与 string 不同,可能包含 \\0 的 bytes 默认以 base64 编码。

// protobuf
"Hello, World!"

// json
"SGVsbG8sIFdvcmxkIQo="

bool

对应 json 的 true / false。

unknown fields

unknown_fields → json 目前不支持,未来可能支持。json → unknown_fields 目前也未支持,即 protobuf 无法透传 json 中不认识的字段。原因在于 protobuf 真正的 key 是 proto 文件中每个字段后的数字:

...
required int32 foo = 3; <-- the real key
...

这也是 unknown_fields 的 key。当一个 protobuf 不认识某个字段时,其 proto 中必然不会有那个数字,所以没办法插入 unknown_fields。

可行的方案有几种:

  • 确保被 json 访问的服务的 proto 文件最新。这样就不需要透传了,但越前端的服务越类似 proxy,可能并不现实。
  • protobuf 中定义特殊透传字段。比如名为 unknown_json_fields,在解析对应的 protobuf 时特殊处理。此方案修改面广且对性能有一定影响,有明确需求时再议。

最后修改于 2022 年 6 月 13 日:更新 getting_start 与 json2pb 文档 (devlive-community/knowforge#73) (b6b734ea7)

评论

登录后参与评论

正在加载评论…