ProtoJSON 格式
Protobuf 支持一种规范的 JSON 编码,从而更容易与不支持标准 protobuf 二进制线格式的系统共享数据。
ProtoJSON 格式不如 protobuf 线格式高效。转换器在编码和解码消息时会消耗更多 CPU,并且(除极少数情况外)编码后的消息会占用更多空间。此外,ProtoJSON 格式会将您的字段和枚举值名称放入编码消息中,这使得将来更改这些名称变得更加困难。移除字段是一项破坏性更改,会触发解析错误。简而言之,Google 几乎所有地方都更倾向于使用标准线格式而非 ProtoJSON 格式,原因有很多。
本主题稍后的表格按类型描述了编码方式。
将 JSON 编码的数据解析为协议缓冲区时,如果某个值缺失或其值为 null
,则会被解释为相应的默认值。单一字段的多个值(使用重复或等效的 JSON 键)会被接受,并且最后一个值将被保留,这与二进制格式解析类似。请注意,并非所有 protobuf JSON 解析器实现都符合规范,某些不符合规范的实现可能会拒绝重复的键。
从协议缓冲区生成 JSON 编码输出时,如果 protobuf 字段具有默认值且该字段不支持字段存在性,则默认情况下会从输出中省略。实现可以提供选项来在输出中包含具有默认值的字段。
已设置值且支持字段存在性的字段始终会在 JSON 编码输出中包含其字段值,即使该值是默认值。例如,使用 optional
关键字定义的 proto3 字段支持字段存在性,如果设置了值,则始终会出现在 JSON 输出中。任何版本的 protobuf 中的消息类型字段都支持字段存在性,如果设置了值,则会出现在输出中。Proto3 隐式存在标量字段仅在其值不等于该类型的默认值时才会出现在 JSON 输出中。
在 JSON 文件中表示数字数据时,如果从线格式解析的数字不适合相应的类型,您将得到与在 C++ 中将数字强制转换为该类型时相同的效果(例如,如果将一个 64 位数字读取为 int32,它将被截断为 32 位)。
下表显示了数据在 JSON 文件中的表示方式。
Protobuf | JSON | JSON 示例 | 注意 |
---|---|---|---|
消息 | 对象 | {"fooBar": v, "g": null, ...} | 生成 JSON 对象。消息字段名称被映射为 lowerCamelCase 并成为 JSON 对象键。如果指定了 json_name 字段选项,则使用指定的值作为键。解析器接受 lowerCamelCase 名称(或由 json_name 选项指定的名称)以及原始 proto 字段名称。对于所有字段类型,null 都是可接受的值,并被视为相应字段类型的默认值。但是,null 不能用于 json_name 值。有关更多信息,请参阅json_name 的更严格验证。 |
枚举 | 字符串 | "FOO_BAR" | 使用 proto 中指定的枚举值名称。解析器同时接受枚举名称和整数值。 |
map<K,V> | 对象 | {"k": v, ...} | 所有键都转换为字符串。 |
repeated V | 数组 | [v, ...] | null 被接受为空列表 [] 。 |
布尔值 | true, false | true, false | |
字符串 | 字符串 | "Hello World!" | |
字节 | base64 字符串 | "YWJjMTIzIT8kKiYoKSctPUB+" | JSON 值是将数据使用标准 base64 编码(带填充)编码为字符串。接受标准或 URL 安全的 base64 编码(带或不带填充)。 |
int32, fixed32, uint32 | 数字 | 1, -10, 0 | JSON 值将是十进制数字。数字或字符串都可接受。空字符串无效。 |
int64, fixed64, uint64 | 字符串 | "1", "-10" | JSON 值将是十进制字符串。数字或字符串都可接受。空字符串无效。 |
float, double | 数字 | 1.1, -10.0, 0, "NaN", "Infinity" | JSON 值将是数字或特殊的字符串值之一:"NaN"、"Infinity" 和 "-Infinity"。数字或字符串都可接受。空字符串无效。指数表示法也接受。 |
Any | 对象 | {"@type": "url", "f": v, ... } | 如果 Any 包含具有特殊 JSON 映射的值,它将按如下方式转换:{"@type": xxx, "value": yyy} 。否则,该值将被转换为 JSON 对象,并插入 "@type" 字段以指示实际数据类型。 |
Timestamp | 字符串 | "1972-01-01T10:00:20.021Z" | 使用 RFC 3339,生成的输出总是 Z 标准化,并使用 0、3、6 或 9 位小数。也接受除 "Z" 之外的偏移量。 |
Duration | 字符串 | "1.000340012s", "1s" | 生成的输出始终包含 0、3、6 或 9 位小数,取决于所需的精度,后跟后缀 "s"。接受任何小数位数(也可以没有),只要它们符合纳秒精度,并且后缀 "s" 是必需的。 |
Struct | 对象 | { ... } | 任何 JSON 对象。请参阅 struct.proto 。 |
包装器类型 | 各种类型 | 2, "2", "foo", true, "true", null, 0, ... | 包装器在 JSON 中使用与被包装原始类型相同的表示,不同之处在于允许 null 并在数据转换和传输过程中保留。 |
FieldMask | 字符串 | "f.fooBar,h" | 请参阅 field_mask.proto 。 |
ListValue | 数组 | [foo, bar, ...] | |
Value | 值 | 任何 JSON 值。查看google.protobuf.Value 了解详细信息。 | |
NullValue | null | JSON null | |
Empty | 对象 | {} | 一个空的 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 输出使用枚举值的名称。可以提供一个选项来改用枚举值的数字值。