ProtoJSON 格式
Protobuf 支持一种规范的 JSON 编码格式,使得与不支持标准 protobuf 二进制格式的系统之间的数据共享更加容易。
ProtoJSON 格式的效率不如 protobuf 二进制格式。转换器在编码和解码消息时会消耗更多的 CPU,并且(除极少数情况外)编码后的消息占用的空间也更大。此外,ProtoJSON 格式会将你的字段名和枚举值名写入编码消息,这会导致后续更改这些名称变得更加困难。移除字段会造成兼容性破坏,并触发解析错误。简而言之,Google 更倾向于在几乎所有场景下使用标准二进制格式而不是 ProtoJSON 格式,是有充分理由的。
各类型的编码方式将在后文的表格中详细说明。
当将 JSON 编码的数据解析为 protocol buffer 时,如果某个值缺失或其值为 null
,则会被解释为对应的默认值。对于单个字段的多次赋值(使用重复或等价的 JSON 键),解析时会保留最后一个值,这与二进制格式解析一致。注意,并非所有 protobuf JSON 解析器实现都符合规范,一些不符合规范的实现可能会拒绝重复键。
当从 protocol buffer 生成 JSON 编码输出时,如果 protobuf 字段为默认值且不支持字段存在性(presence),则默认情况下不会在输出中包含该字段。实现可以提供选项,将默认值字段包含在输出中。
已设置值且支持字段存在性的字段,在 JSON 编码输出中总会包含该字段,即使其为默认值。例如,proto3 中用 optional
关键字定义的字段支持存在性,如果被设置,则总会出现在 JSON 输出中。任何版本的 protobuf 中的消息类型字段都支持存在性,如果被设置也会出现在输出中。proto3 中隐式存在性的标量字段,只有在其值不是该类型的默认值时才会出现在 JSON 输出中。
在 JSON 文件中表示数值数据时,如果从二进制格式解析出的数字不适合对应的类型,将会像在 C++ 中强制类型转换一样(例如,将 64 位数字读取为 int32 时会被截断为 32 位)。
下表展示了各类数据在 JSON 文件中的表示方式。
Protobuf | JSON | JSON 示例 | 说明 |
---|---|---|---|
message | object | {"fooBar": v, "g": null, ...} | 生成 JSON 对象。消息字段名会转换为 lowerCamelCase,作为 JSON 对象的键。如果指定了 json_name 字段选项,则使用指定值作为键。解析器同时接受 lowerCamelCase 名(或 json_name 指定的名称)和原始 proto 字段名。null 是所有字段类型的可接受值,并被视为对应字段类型的默认值。但 null 不能用于 json_name 。详情见json_name 更严格校验。 |
enum | string | "FOO_BAR" | 使用 proto 中指定的枚举值名称。解析器同时接受枚举名称和整数值。 |
map<K,V> | object | {"k": v, ...} | 所有键都转换为字符串。 |
repeated V | array | [v, ...] | null 被视为空列表 [] 。 |
bool | true, false | true, false | |
string | string | "Hello World!" | |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" | JSON 值为使用标准 base64 编码(带填充)的字符串。解析时可接受标准或 URL 安全的 base64 编码(带或不带填充)。 |
int32, fixed32, uint32 | number | 1, -10, 0 | JSON 值为十进制数字。解析时可接受数字或字符串。空字符串无效。 |
int64, fixed64, uint64 | string | "1", "-10" | JSON 值为十进制字符串。解析时可接受数字或字符串。空字符串无效。 |
float, double | number | 1.1, -10.0, 0, "NaN", "Infinity" | JSON 值为数字或特殊字符串 "NaN"、"Infinity"、"-Infinity"。解析时可接受数字或字符串。空字符串无效。也支持指数表示法。 |
Any | object | {"@type": "url", "f": v, ... } | 如果 Any 包含有特殊 JSON 映射的值,则转换为 {"@type": xxx, "value": yyy} 。否则,值会被转换为 JSON 对象,并插入 "@type" 字段以指示实际数据类型。 |
Timestamp | string | "1972-01-01T10:00:20.021Z" | 使用 RFC 3339 格式,输出总是 Z 标准化,并使用 0、3、6 或 9 位小数。也接受非 "Z" 的时区偏移。 |
Duration | string | "1.000340012s", "1s" | 输出总是包含 0、3、6 或 9 位小数,具体取决于所需精度,后缀为 "s"。解析时可接受任意小数位(包括无小数),只要精度不超过纳秒,且必须有 "s" 后缀。 |
Struct | object | { ... } | 任意 JSON 对象。参见 struct.proto 。 |
Wrapper types | 多种类型 | 2, "2", "foo", true, "true", null, 0, ... | 包装类型在 JSON 中的表示与被包装的原始类型相同,区别在于 null 允许且在数据转换和传输过程中会被保留。 |
FieldMask | string | "f.fooBar,h" | 参见 field_mask.proto 。 |
ListValue | array | [foo, bar, ...] | |
Value | value | 任意 JSON 值。详见 google.protobuf.Value。 | |
NullValue | null | JSON null | |
Empty | object | {} | 空的 JSON 对象 |
JSON 选项
符合规范的 protobuf JSON 实现可以提供以下选项:
总是输出无存在性的字段:默认情况下,不支持存在性的字段且为默认值时不会出现在 JSON 输出中(如隐式存在性的整数为 0,字符串为空,repeated 和 map 字段为空)。实现可以提供选项,强制输出这些默认值字段。
截至 v25.x,C++、Java 和 Python 实现尚不完全符合规范,因为该选项会影响 proto2 的
optional
字段,但不会影响 proto3 的optional
字段。未来版本将修复此问题。忽略未知字段:protobuf JSON 解析器默认应拒绝未知字段,但可以提供选项,在解析时忽略未知字段。
使用 proto 字段名而非 lowerCamelCase 名称:默认情况下,protobuf JSON 打印器会将字段名转换为 lowerCamelCase 并作为 JSON 名称。实现可以提供选项,使用 proto 字段名作为 JSON 名称。protobuf JSON 解析器必须同时接受 lowerCamelCase 名称和 proto 字段名。
将枚举值以整数而非字符串输出:默认情况下,JSON 输出使用枚举值的名称。实现可以提供选项,使用枚举值的数字值输出。