brpc JSON 与 Protobuf 双向转化完全指南:json2pb 模块规则与源码剖析

发布时间:2026/9/13 13:31:53
brpc JSON 与 Protobuf 双向转化完全指南:json2pb 模块规则与源码剖析 brpc JSON 与 Protobuf 双向转化完全指南json2pb 模块规则与源码剖析【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. brpc means better RPC.项目地址: https://gitcode.com/GitHub_Trending/brpc/brpcbrpc 内置了 JSON 与 Protobuf 之间的双向转化能力实现位于 src/json2pb 目录JSON 解析基于 rapidjson。这一能力是通过 HTTP JSON 访问 Protobuf 服务这一对外服务常见形态的基础转化是否精准直接决定接口可用性。本文基于 docs/cn/json2pb.md 整理出完整的转化规则并对照源码逐一剖析其实现原理读完你既能正确编写 JSON 请求体也能理解 brpc 在序列化与反序列化时的底层行为。该功能对 Protobuf 2.x 与 3.x 均有效3.x 另内置了官方 JSON 转化brpc 亦提供对应封装。总览JSON 与 Protobuf 的类型映射关系brpc 的 json2pb 模块采用按值类型推断而非机械匹配的转化策略rapidjson 会对 JSON 中的数值打上类型标记转化代码据此自动选择可容纳该值的 Protobuf 字段类型同时天然识别 overflow / underflow 并让转化失败。其整体映射如下Protobuf 类型JSON 表示说明messageObject花括号{}元素递归解析repeated fieldArray方括号[]元素类型相同map 型 repeatedObject{k: v}形式条件见下文map一节int32 / uint32 / int64 / uint64Int / UInt / Int64 / UInt64按值自动适配float / doubleFloat / Double / Int / Uint / Int64 / Uint64整数也可转浮点enum整数 或 枚举名字符串默认输出名字可由Pb2JsonOptions.enum_option控制stringString默认同名转化非法 C 变量名按特殊规则编码bytes字符串base64默认 base64 编码booltrue/false—注意上述为 json2pb 默认转化规则。brpc 同时提供了 Protobuf 官方 ProtoJSON 格式的转化入口ProtoJsonToProtoMessage/ProtoMessageToProtoJson本文主体聚焦默认的 json2pb 规则。message递归解析的花括号对象message 对应 rapidjson 的 Object以花括号包围其中的元素会被递归地解析。嵌套 message 的字段名在 JSON 中直接使用 proto 中的字段名// 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; }{foo:{field1:hello, field2:3},name:Tom }其中flag未出现在 JSON 中作为 optional 字段可以省略而foo作为一个嵌套 message 被递归解析成对象。JSON 与 Protobuf 的入口函数分别是 json_to_pb.h 中声明的JsonToProtoMessage与 pb_to_json.h 中声明的ProtoMessageToJson两者均提供以std::string、ZeroCopyInputStream/OutputStream为输入的多种重载便于接入不同 I/O 场景。repeated field方括号数组与单 repeated 转数组特性repeated field 对应 rapidjson 的 Array以方括号包围其中的元素会被递归解析与 message 不同数组中每个元素类型相同// protobuf repeated int32 numbers 1;{numbers : [12, 17, 1, 24] }特别地针对仅有一个repeated类型成员的 message序列化为 JSON 时支持直接序列化为数组以简化包体// protobuf message Foo { repeated int32 numbers 1; }[12, 17, 1, 24]该特性默认为关闭状态客户端发送请求或服务端发送回复时均可手动开启brpc::Controller cntl; cntl.set_pb_single_repeated_to_array(true);从源码看该开关对应 controller.h 中的set_pb_single_repeated_to_array它在 http_rpc_protocol.cpp 中被翻译为Pb2JsonOptions::single_repeated_to_arrayJSON 输出方向并在 http_rpc_protocol.cpp 中被翻译为Json2PbOptions::array_to_single_repeatedJSON 输入方向。两个方向的选项定义分别位于 pb_to_json.h 与 json_to_pb.h默认值均为 false与文档描述一致。map符合特定条件的 repeated MSG 视作 JSON map满足全部如下条件的 repeated message 会被视作 JSON mapMSG 包含一个名为key的字段类型为 stringtag 为 1MSG 包含一个名为value的字段tag 为 2不包含其他字段。该判断在源码中由 protobuf_map.cpp 的IsProtobufMap完成其契约key必须 tag1、value必须 tag2、必须是 string key注释在 protobuf_map.h因为 JSON 只支持字符串 key所以key字段类型必须是 stringmap 在 JSON 中呈现为{my_map: {key1: value1, key2: value2}}而非[{key: key1, value: value1}, ...]的数组形式。这种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使条件不再满足。另外pb_to_json.h 中的Pb2JsonOptions::enable_protobuf_map选项默认 true也控制着 map 转化的开关。integers按值自动适配类型并识别溢出rapidjson 会根据数值打上对应的类型标记例如对于3rapidjson 中的IsUInt、IsInt、IsUint64、IsInt64等函数均返回 true对于-1IsUInt和IsUint64返回 false对于5000000000IsUInt和IsInt为 false。这使得转化代码无需特殊处理即可自动把 JSON 中的 UInt 填入 Protobuf 中的 int64而不是机械地认为两个类型不匹配。相应地转化代码能自动识别 overflow 和 underflow出现时转化失败。// protobuf int32 uint32 int64 uint64Int UInt Int64 UInt64也就是说JSON 侧的整数类型与 Protobuf 侧按值的可容纳性匹配超出目标类型表示范围的数值会直接导致转化失败并返回错误信息避免静默截断。floating point整数可转浮点支持特殊值字符串JSON 的整数类型也可以转至 Protobuf 的浮点数类型。浮点数IEEE754除了普通数字外还接受NaN、Infinity、-Infinity三个字符串分别对应 Not A Number、正无穷、负无穷。// protobuf float doubleFloat Double Int Uint Int64 Uint64这意味着在 JSON 侧你可以直接写{score: 100}让整数填入 float/double 字段也可以用NaN、Infinity表达 IEEE754 特殊值。enum按名字或按数字输出enum 可以转化为整数也可以转化为其名字对应的字符串由Pb2JsonOptions.enum_option控制默认输出名字。枚举定义见 pb_to_json.henum EnumOption { OUTPUT_ENUM_BY_NAME 0, // 按名字输出默认 OUTPUT_ENUM_BY_NUMBER 1, // 按数值输出 };在 HTTP 协议栈中该选项受全局 gflagFLAGS_pb_enum_as_number默认 false控制见 http_rpc_protocol.cpp 与 http_rpc_protocol.cpp当pb_enum_as_numbertrue时按数字输出枚举否则按名字输出。反序列化方向JSON → enum则同时接受整数和枚举名对应的字符串。string同名转化与非法变量名编码规则默认情况下JSON 字段名与 Protobuf 字段名同名转化。但当 JSON 中出现非法 C 变量名即不符合 pb 变量名规则时转化仍然允许规则是illegal-char - **_Z**ASCII-of-the-char**_**即保留原位置的小写字母、大写字母、数字和_将其他特殊字符替换为_Z 该字符的十进制 ASCII 码 _。该规则在 encode_decode.h 中有详细注释pattern:_Zxxx_规则保留原始小写、大写、数字字符和_在原位置其他特殊字符改为_Zxxx_xxx 为字符的十进制数字。例如abc123_ABC-转化为abc123_ABC_Z045_。对应的encode_name/decode_name函数encode_decode.h返回 false 表示无需编码、true 表示需要编码返回 false 时encoded_content不会被改动。这样proto 中不合法但 JSON 场景下又必须出现的字段名如带连字符的 HTTP 头部风格字段名可以无损往返。bytes默认 base64 编码与 string 不同可能包含\0的 bytes 字段默认以base64编码// protobuf Hello, World!SGVsbG8sIFdvcmxkIQo从源码看json_to_pb.h 的Json2PbOptions::base64_to_bytes控制 JSON→pb 方向是否对 bytes 类型做 base64 解码pb_to_json.h 的Pb2JsonOptions::bytes_to_base64控制 pb→JSON 方向是否 base64 编码注释标明默认值在 baidu 内部为 false、外部为 true。HTTP 协议栈中两个方向统一由cntl-has_pb_bytes_to_base64()控制见 http_rpc_protocol.cpp 与 http_rpc_protocol.cpp。bool对应 JSON 的 true / falsebool 类型对应 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 时特殊处理。此方案修改面广且对性能有一定影响有明确需求时再议。另外值得注意的是brpc 的ProtoJSON路径ProtoJsonToProtoMessage在 HTTP 解析时默认设置了ignore_unknown_fields true见 http_rpc_protocol.cpp即官方 JSON 格式下未知字段会被静默忽略而 json2pb 默认路径遇到未知字段则无法透传两者行为不同选型时需留意。实战HTTP JSON 访问 Protobuf 服务时的转化链路通过 HTTP JSON 访问 Protobuf 服务时上述全部规则在 http_rpc_protocol.cpp 中落地请求方向JSON → PBJsonToProtoMessagehttp_rpc_protocol.cpp构造Json2PbOptions将cntl上的pb_bytes_to_base64、pb_single_repeated_to_array标志映射到base64_to_bytes、array_to_single_repeated解析失败时以ERESPONSE/EREQUEST错误码SetFailed并附上带完整 message 名的错误描述响应方向PB → JSONProtoMessageToJsonhttp_rpc_protocol.cpp构造Pb2JsonOptions将pb_bytes_to_base64、pb_jsonify_empty_array、always_print_primitive_fields、pb_single_repeated_to_array、pb_enum_as_number全部映射到位。也就是说业务代码只需通过brpc::Controller上的set_*系列方法如 controller.h 的set_pb_single_repeated_to_array即可控制 JSON 转化的行为细节无需直接操作 json2pb 库函数需要精细控制时也可绕过 HTTP 层直接调用 json_to_pb.h / pb_to_json.h 中公开的 API。小结brpc 的 json2pb 模块以按值类型推断为设计核心覆盖了 message、repeated、map、integer、浮点、enum、string、bytes、bool 与 unknown fields 的全部转化规则其行为均可通过Pb2JsonOptions/Json2PbOptions两个选项结构精细调节并在 HTTP 协议栈中与brpc::Controller的开关一一对应。理解这些规则是保证 JSON 接口与 Protobuf 服务数据不出错的前提——尤其是单 repeated 转数组的默认关闭、map 的强约束条件、bytes 的 base64 编码、enum 的按名/按数输出以及 unknown fields 无法透传这一限制。【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. brpc means better RPC.项目地址: https://gitcode.com/GitHub_Trending/brpc/brpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考