本文翻译自 ProtoJSON Format | Protocol Buffers Documentation
Protobuf 支持规范的 JSON 编码格式,便于与不支持标准 Protobuf 二进制有线格式的系统共享数据。
ProtoJSON 格式不如 Protobuf 有线格式高效。转换器在编解码消息时会消耗更多 CPU 资源,且(除少数情况外)编码后的消息占用空间更大。此外,ProtoJSON 格式会将字段名和枚举值名写入编码消息,导致后续重命名这些标识符变得极其困难。删除字段更是破坏性变更,会引发解析错误。简言之,Google 坚持使用标准有线格式而非 ProtoJSON 格式有诸多充分理由。
下文表格按类型详细描述了编码规则。
解析与生成规则
将 JSON 编码数据解析为 Protocol Buffer 时:
- 若值缺失或为
null
,将被解释为对应字段的默认值 - 单数字段允许多个值(通过重复键或等效 JSON 键),保留最后一个值(与二进制格式解析行为一致)
- ⚠️ 注意:非所有解析器实现都符合此规范,部分实现可能拒绝重复键
从 Protocol Buffer 生成 JSON 编码数据时:
- 默认值字段:若字段不支持存在性检测,默认省略该字段(实现方可提供选项强制输出)
- 显式设置字段:支持存在性检测的字段(如带
optional
关键字的 proto3 字段、任意版本的 message 类型字段)始终输出,即使值为默认值 - Proto3 隐式存在标量字段:仅当值非类型默认值时输出
- 数值处理:若解析值超出目标类型范围,将按 C++ 强制转换规则处理(如 64 位整数转为 int32 时将被截断)
JSON 数据表示对照表
Protobuf 类型 | JSON 类型 | JSON 示例 | 说明 |
---|---|---|---|
message | object | {"fooBar": v, "g": null, ...} |
• 字段名转换为小驼峰命名(lowerCamelCase)作为 JSON 键 • 若指定 json_name 字段选项,则使用该值作为键• 解析器同时接受小驼峰名(或 json_name 值)和原始字段名• null 会被解释为字段默认值• ⚠️ json_name 禁止设为 null (详见严格校验说明)
|
enum | string | "FOO_BAR" |
• 使用 proto 定义的枚举值名 • 解析器同时接受枚举名和整数值 |
map<K,V> | object | {"k": v, ...} |
所有键均转换为字符串 |
repeated V | array | [v, ...] |
null 等价于空列表 [] |
bool | boolean | true, false |
- |
string | string | "Hello World!" |
- |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" |
• 输出:标准 base64 编码(含填充) • 输入:接受标准/URL安全 base64(含/不含填充) |
int32, fixed32, uint32 | number | 1, -10, 0 |
• 接受数字或字符串 • 空字符串无效 |
int64, fixed64, uint64 | string | "1", "-10" |
• 接受数字或字符串 • 空字符串无效 |
float, double | number/string | 1.1, "NaN", "-Infinity" |
• 接受数字或特殊字符串("NaN"/"Infinity"/"-Infinity") • 支持科学计数法 • 空字符串无效 |
Any | object | {"@type": "url", "value": yyy} |
含特殊 JSON 映射的值按固定结构转换,其他值转换后插入 "@type" 字段标识类型 |
Timestamp | string | "1972-01-01T10:00:20.021Z" |
• 输出:RFC 3339 格式(Z 标准化,含 0/3/6/9 位小数) • 输入:接受非 "Z" 时区偏移 |
Duration | string | "1.000340012s" |
• 输出:含 0/3/6/9 位小数 + "s" 后缀 • 输入:接受任意精度纳秒值 + "s" 后缀 |
Struct | object | { ... } |
任意 JSON 对象(参见 struct.proto ) |
Wrapper types | 对应基础类型 | 2, "foo", true, null |
包装类型使用原始类型表示规则,但允许保留 null 值 |
FieldMask | string | "f.fooBar,h" |
参见 field_mask.proto |
ListValue | array | [foo, bar, ...] |
- |
Value | any | 任意值 | 任意 JSON 值(详见 google.protobuf.Value) |
NullValue | null | null |
JSON null |
Empty | object | {} |
空 JSON 对象 |
JSON 选项 {#json-options}
符合规范的 Protobuf JSON 实现可提供以下选项:
始终输出无存在性字段
- 默认行为:省略不支持存在性检测且为默认值的字段(如 proto3 隐式存在字段的 0 值、空字符串、空列表/映射)
- 选项:可覆盖默认行为,强制输出含默认值的字段
- ⚠️ 现状:截至 v25.x,C++/Java/Python 实现存在不一致性(影响 proto2 optional 但不影响 proto3 optional),后续版本将修复
忽略未知字段
- 默认行为:拒绝未知字段
- 选项:解析时忽略未知字段
使用 proto 字段名替代小驼峰命名
- 默认行为:字段名转为小驼峰命名作为 JSON 键
- 选项:直接使用 proto 字段名作为 JSON 键
- ⚠️ 要求:解析器必须同时接受小驼峰名和原始字段名
枚举值输出为整数
- 默认行为:输出枚举值名称
- 选项:输出枚举值对应的数字