ProtoJSON 格式

介绍如何使用 Protobuf 到 JSON 的转换工具。

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 文件中的表示方式。

ProtobufJSONJSON 示例说明
messageobject{"fooBar": v, "g": null, ...}生成 JSON 对象。消息字段名会转换为 lowerCamelCase,作为 JSON 对象的键。如果指定了 json_name 字段选项,则使用指定值作为键。解析器同时接受 lowerCamelCase 名(或 json_name 指定的名称)和原始 proto 字段名。null 是所有字段类型的可接受值,并被视为对应字段类型的默认值。但 null 不能用于 json_name。详情见json_name 更严格校验
enumstring"FOO_BAR"使用 proto 中指定的枚举值名称。解析器同时接受枚举名称和整数值。
map<K,V>object{"k": v, ...}所有键都转换为字符串。
repeated Varray[v, ...]null 被视为空列表 []
booltrue, falsetrue, false
stringstring"Hello World!"
bytesbase64 string"YWJjMTIzIT8kKiYoKSctPUB+"JSON 值为使用标准 base64 编码(带填充)的字符串。解析时可接受标准或 URL 安全的 base64 编码(带或不带填充)。
int32, fixed32, uint32number1, -10, 0JSON 值为十进制数字。解析时可接受数字或字符串。空字符串无效。
int64, fixed64, uint64string"1", "-10"JSON 值为十进制字符串。解析时可接受数字或字符串。空字符串无效。
float, doublenumber1.1, -10.0, 0, "NaN", "Infinity"JSON 值为数字或特殊字符串 "NaN"、"Infinity"、"-Infinity"。解析时可接受数字或字符串。空字符串无效。也支持指数表示法。
Anyobject{"@type": "url", "f": v, ... }如果 Any 包含有特殊 JSON 映射的值,则转换为 {"@type": xxx, "value": yyy}。否则,值会被转换为 JSON 对象,并插入 "@type" 字段以指示实际数据类型。
Timestampstring"1972-01-01T10:00:20.021Z"使用 RFC 3339 格式,输出总是 Z 标准化,并使用 0、3、6 或 9 位小数。也接受非 "Z" 的时区偏移。
Durationstring"1.000340012s", "1s"输出总是包含 0、3、6 或 9 位小数,具体取决于所需精度,后缀为 "s"。解析时可接受任意小数位(包括无小数),只要精度不超过纳秒,且必须有 "s" 后缀。
Structobject{ ... }任意 JSON 对象。参见 struct.proto
Wrapper types多种类型2, "2", "foo", true, "true", null, 0, ...包装类型在 JSON 中的表示与被包装的原始类型相同,区别在于 null 允许且在数据转换和传输过程中会被保留。
FieldMaskstring"f.fooBar,h"参见 field_mask.proto
ListValuearray[foo, bar, ...]
Valuevalue任意 JSON 值。详见 google.protobuf.Value
NullValuenullJSON null
Emptyobject{}空的 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 输出使用枚举值的名称。实现可以提供选项,使用枚举值的数字值输出。