gRPC 与 JSON 之间的类型映射规则
基本映射
1. 基本类型映射
Protobuf 类型 | JSON 类型 | 示例(Protobuf → JSON) | 说明 |
---|---|---|---|
int32 , sint32 , uint32 | 数字 | 123 | 直接映射为 JSON 数字,范围在 -2^31 到 2^32-1 之间。 |
int64 , sint64 , uint64 | 字符串 | "9223372036854775807" | 必须用字符串表示,避免 JavaScript 等语言中的精度丢失(安全范围为 ±2^53-1 )。 |
float , double | 数字 | 3.14 | 浮点数直接映射为 JSON 数字。 |
bool | true /false | true | 布尔值直接映射。 |
string | 字符串 | "hello" | UTF-8 编码的字符串。 |
bytes | 字符串(Base64) | "SGVsbG8gd29ybGQ=" | 二进制数据使用标准 Base64 编码。 |
2. 枚举类型(enum
)
枚举类型可以映射为两种形式:
2.1 数值形式(默认)
- Protobuf:
enum Status { ACTIVE = 0; INACTIVE = 1; }
- JSON:
{ "status": 0 }
或{ "status": 1 }
2.2 字符串形式(需配置)
通过设置 json_format
选项,可将枚举值映射为字符串:
import "google/protobuf/descriptor.proto";extend google.protobuf.EnumValueOptions {string json_name = 50000;
}enum Status {ACTIVE = 0 [(json_name) = "active"];INACTIVE = 1 [(json_name) = "inactive"];
}
- JSON:
{ "status": "active" }
或{ "status": "inactive" }
3. 消息类型(message
)
- Protobuf:
message Person {string name = 1;int32 age = 2; }
- JSON:
{"name": "Alice","age": 30 }
- 字段命名转换:Protobuf 的蛇形命名(如
user_name
)会自动转换为 JSON 的驼峰命名(如userName
),可通过json_name
选项自定义。
4. 重复字段(repeated
)
- Protobuf:
message Person {repeated string hobbies = 1; }
- JSON:
{"hobbies": ["reading", "coding"] }
5. 映射字段(map
)
- Protobuf:
message Person {map<string, string> metadata = 1; }
- JSON:
{"metadata": {"role": "admin","level": "expert"} }
6. 特殊类型(google.protobuf
包)
6.1 Timestamp
- Protobuf:
import "google/protobuf/timestamp.proto";message Event {google.protobuf.Timestamp time = 1; }
- JSON:ISO 8601 格式的字符串
{"time": "2023-01-01T12:00:00Z" }
6.2 Duration
- Protobuf:
import "google/protobuf/duration.proto";message Task {google.protobuf.Duration timeout = 1; }
- JSON:带单位的字符串
{"timeout": "3600s" // 或 "1h"、"1.5h" 等 }
6.3 Any
用于包装任意 Protobuf 消息:
- Protobuf:
import "google/protobuf/any.proto";message Response {google.protobuf.Any data = 1; }
- JSON:
{"data": {"@type": "type.googleapis.com/your.package.MessageType","field1": "value1","field2": "value2"} }
6.4 Struct
动态结构(类似 JSON 对象):
- Protobuf:
import "google/protobuf/struct.proto";message Config {google.protobuf.Struct settings = 1; }
- JSON:直接嵌套 JSON 对象
{"settings": {"debug": true,"timeout": 3000} }
7. 空值处理
- 默认值:未设置的字段在 JSON 中可以省略(如
int32
默认为0
,string
默认为空字符串)。 - 显式空值:通过
google.protobuf.NullValue
类型表示null
:import "google/protobuf/wrappers.proto";message OptionalField {google.protobuf.StringValue name = 1; // 允许 null }
{"name": null }
特殊映射
1. 超出 JavaScript 安全范围的数值类型
Protobuf 类型 | JSON 类型 | 原因 |
---|---|---|
int64 , sint64 | 字符串 | 范围为 -2^63 到 2^63-1 ,超过 JavaScript 的安全整数范围 ±2^53-1 。 |
uint64 | 字符串 | 范围为 0 到 2^64-1 ,同样超出 JavaScript 安全范围。 |
fixed64 , sfixed64 | 字符串 | 64 位固定宽度整数,也会被转为字符串。 |
2. 二进制类型
Protobuf 类型 | JSON 类型 | 原因 |
---|---|---|
bytes | 字符串(Base64) | JSON 无法直接表示二进制数据,需通过 Base64 编码转换为字符串。 |
3. Google Protobuf 标准类型
Protobuf 类型 | JSON 类型 | 示例格式 |
---|---|---|
google.protobuf.Timestamp | 字符串 | 2023-01-01T12:00:00Z (ISO 8601 格式) |
google.protobuf.Duration | 字符串 | 3600s 、1.5h 、5m30s (带单位的字符串) |
google.protobuf.Any | 对象 | { "@type": "...", "field": "value" } (包含类型信息) |
google.protobuf.Struct | 对象 | 直接映射为 JSON 对象(用于动态结构) |
google.protobuf.Value | 任意类型 | 根据实际值类型(如 { "numberValue": 123 } ) |
google.protobuf.ListValue | 数组 | JSON 数组(用于动态列表) |
4. 其他特殊情况
4.1 枚举类型
- 默认映射为 数值(如
{ "status": 1 }
)。 - 可通过
json_name
选项配置为 字符串(如{ "status": "ACTIVE" }
)。
4.2 空值(Null)
- 使用
google.protobuf.NullValue
表示null
:{"optionalField": null }
4.3 浮点特殊值
NaN
、Infinity
、-Infinity
在 JSON 中通常用字符串表示(如"NaN"
),但具体实现可能因语言而异。
为什么这些类型需要特殊处理?
- 跨语言兼容性:JavaScript、Python 等语言的数值类型范围与 Protobuf 不匹配,需要通过字符串避免精度丢失。
- 格式标准化:时间、持续时间等类型通过固定格式(如 ISO 8601)确保不同系统间正确解析。
- 类型信息保留:
Any
类型需要在 JSON 中保留原始消息的类型信息(通过@type
字段)。
总结
gRPC 的 JSON 映射规则在保持 Protobuf 强类型特性的同时,通过特殊转换确保与 JSON 弱类型系统的兼容性。这些规则是跨平台 API 开发的基础,特别是在处理跨语言数据交换时尤为重要。