brpc/docs/cn/json2pb.md
2022-12-14 20:13:26 +08:00

142 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

brpc支持json和protobuf间的**双向**转化,实现于[json2pb](https://github.com/brpc/brpc/tree/master/src/json2pb/)json解析使用[rapidjson](https://github.com/miloyip/rapidjson)。此功能对pb2.x和3.x均有效。pb3内置了[转换json](https://developers.google.com/protocol-buffers/docs/proto3#json)的功能。
by design, 通过HTTP + json访问protobuf服务是对外服务的常见方式故转化必须精准转化规则列举如下。
## message
对应rapidjson Object, 以花括号包围,其中的元素会被递归地解析。
```protobuf
// protobuf
message Foo {
required string field1 = 1;
required int32 field2 = 2;
}
message Bar {
required Foo foo = 1;
optional bool flag = 2;
required string name = 3;
}
// rapidjson
{"foo":{"field1":"hello", "field2":3},"name":"Tom" }
```
## repeated field
对应rapidjson Array, 以方括号包围其中的元素会被递归地解析和message不同每个元素的类型相同。
```protobuf
// protobuf
repeated int32 numbers = 1;
// rapidjson
{"numbers" : [12, 17, 1, 24] }
```
特别的,针对仅有一个 `repeated` 类型成员的 `message`,序列化为 `json` 时支持直接序列化为数组,以简化包体。
```protobuf
// protobuf
message Foo {
repeated int32 numbers = 1;
}
// rapidjson
[12, 17, 1, 24]
```
该特性默认为关闭状态,客户端在发送请求时,或服务端在发送回复时,可手动开启:
```c++
brpc::Controller cntl;
cntl.set_pb_single_repeated_to_array(true);
```
## map
满足如下条件的repeated MSG被视作json map :
- MSG包含一个名为key的字段类型为stringtag为1。
- MSG包含一个名为value的字段tag为2。
- 不包含其他字段。
这种"map"的属性有:
- 自然不能确保key有序或不重复用户视需求自行检查。
- 与protobuf 3.x中的map二进制兼容故3.x中的map使用pb2json也会正确地转化为json map。
如果符合所有条件的repeated MSG并不需要被认为是json map打破上面任一条件就行了: 在MSG中加入optional int32 this_message_is_not_map_entry = 3; 这个办法破坏了“不包含其他字段”这项且不影响二进制兼容。也可以调换key和value的tag值让前者为2后者为1也使条件不再满足。
## integers
rapidjson会根据值打上对应的类型标记比如
* 对于3rapidjson中的IsUInt, IsInt, IsUint64, IsInt64等函数均会返回true。
* 对于-1则IsUInt和IsUint64会返回false。
* 对于5000000000IsUInt和IsInt是false。
这使得我们不用特殊处理转化代码就可以自动地把json中的UInt填入protobuf中的int64而不是机械地认为这两个类型不匹配。相应地转化代码自然能识别overflow和underflow当出现时会转化失败。
```protobuf
// protobuf
int32 uint32 int64 uint64
// rapidjson
Int UInt Int64 UInt64
```
## floating point
json的整数类型也可以转至pb的浮点数类型。浮点数(IEEE754)除了普通数字外还接受"NaN", "Infinity", "-Infinity"三个字符串分别对应Not A Number正无穷负无穷。
```protobuf
// protobuf
float double
// rapidjson
Float Double Int Uint Int64 Uint64
```
## enum
enum可转化为整数或其名字对应的字符串可由Pb2JsonOptions.enum_options控制。默认后者。
## string
默认同名转化。但当json中出现非法C++变量名pb的变量名规则允许转化规则是:
`illegal-char <-> **_Z**<ASCII-of-the-char>**_**`
## bytes
和string不同可能包含\0的bytes默认以base64编码。
```protobuf
// protobuf
"Hello, World!"
// json
"SGVsbG8sIFdvcmxkIQo="
```
## bool
对应json的true false
## unknown fields
unknown_fields → json目前不支持未来可能支持。json → unknown_fields目前也未支持即protobuf无法透传json中不认识的字段。原因在于protobuf真正的key是proto文件中每个字段后的数字:
```protobuf
...
required int32 foo = 3; <-- the real key
...
```
这也是unknown_fields的key。当一个protobuf不认识某个字段时其proto中必然不会有那个数字所以没办法插入unknown_fields。
可行的方案有几种:
- 确保被json访问的服务的proto文件最新。这样就不需要透传了但越前端的服务越类似proxy可能并不现实。
- protobuf中定义特殊透传字段。比如名为unknown_json_fields在解析对应的protobuf时特殊处理。此方案修改面广且对性能有一定影响有明确需求时再议。