Protocol Buffers V3 版本和 V2 相比有了比较大的变化,删除了 V2 的一些特性。 这是源自 Language Guide(proto2) 的 V2 版本语法要点,建议使用 V3 版本 Protocol Buffers V3 协议语法要点记录。
安装了 protobuf 插件,IntelliJ IDEA 还是不能识别 protobuf 的 import:
Protocol Buffer imports not recognized in Intellij 给出的方法有效:
代码生成命令,IMPORT_PATH 指定查找 proto 文件的路径:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
package 声明文件的包名:
package foo.bar;
message Open { ... }
其它 package 通过 package name 引用包内的定义:
message Foo {
required foo.bar.Open open = 1;
}
用 import 导入目标文件:
import "myproject/other_protos.proto";
A 文件通过 import public
引用 B 文件后,只需引用 A 文件就可以直接使用 B 文件中的定义。
import public 不支持 java,生成 java 代码时不能使用该功能。
// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
proto2 可以引用 proto3 的定义,proto3 不能引用 proto2 中的 enum。
message SearchResponse {
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
repeated Result result = 1;
}
// 引用 SearchResponse 中定义的 Result
message SomeOtherMessage {
optional SearchResponse.Result result = 1;
}
option 分为file-level
、message-level
和 field-level
三种级别,分别在不同的位置使用。
google/protobuf/descriptor.proto 介绍了所有 option 的用法。
file 级别的 options 用法:
option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;
// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;
message 级别的 option 用法:
message Foo {
option message_set_wire_format = true;
extensions 4 to max;
}
field 级别的 option 用法:
repeated int32 samples = 4 [packed = true];
optional int32 old_field = 6 [deprecated=true];
可以通过扩展 google.protobuf.XXXOptions 增加自定义的 option:
import "google/protobuf/descriptor.proto";
extend google.protobuf.MessageOptions {
optional string my_option = 51234; // 自定义 message-level option
}
message MyMessage {
option (my_option) = "Hello world!";
}
rpc 接口定义方法:
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
field 的编号:
注意事项 :
extensions 用来预留可以被第三方使用的字段编号:
message Foo {
// ...
extensions 100 to 199;
}
// 另一个 proto 文件,扩展 Foo:
extend Foo {
optional int32 bar = 126; // 扩展的 field 不能是 oneof、map
}
支持的类型:Scalar Value Types
double
float
int32
int64
uint32
uint64
sint32: 对负数的编码更高效
sint64: 对负数的编码更高效
fixed32:永远 4 字节,对应大于 2^28 的数值编码更高效
fixed64:永远 8 字节,对应大于 2^56 的数值编码更高效
sfixed32: 永远 4 字节
sfixed64: 永远 8 字节
bool:
string:UTF-8 或者 7-bit ASCII Text
bytes:
field 类型:
repeated int32 samples = 4 [packed = true];
用 reserved 避免曾经使用过后来被删除的 field 的编号被再次使用:
message Foo {
reserved 2, 15, 9 to 11; // 使用编号
reserved "foo", "bar"; // 或者名称,但是编号和名称不同同时混用
}
enum Foo {
reserved 2, 15, 9 to 11, 40 to max; // 用 max 表示最大值
reserved "FOO", "BAR";
}
proto3删除了该语法
用 default 设置 optional field 缺失时的默认数值,如果未设置 default 使用类型默认值:
optional int32 result_per_page = 3 [default = 10];
map<key_type, value_type> map_field = N;
map 的注意事项:
map 等价于下面的定义:
message MapFieldEntry {
optional key_type key = 1;
optional value_type value = 2;
}
repeated MapFieldEntry map_field = N;
enum 使用 32bit,使用 varint 编码,对负数编码效率低,不建议使用负数。
enum 可以独立定义,也可以在 message 内部定义:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
optional Corpus corpus = 4 [default = UNIVERSAL];
}
支持别名:
enum EnumAllowingAlias {
// 枚举值存在重复时,如果不指定 allow_alias=true,会报错
option allow_alias = true;
EAA_UNSPECIFIED = 0;
EAA_STARTED = 1;
EAA_RUNNING = 1;
EAA_FINISHED = 2;
}
## oneof 类型
oneof 中的 field 不能直接使用 requried、optional、repeated,可以使用内部使用了这些修饰符的 message。
```proto
message SampleMessage {
oneof test_oneof {
// oneof 内不能使用 requried、optional、repeated 修饰
string name = 4;
// SubMessage 内部的 field 可以用 requried、optional、repeated 修饰
SubMessage sub_message = 9;
}
}
group 不建议使用了,用嵌套定义替代 :
message SearchResponse {
repeated group Result = 1 { // group 不建议使用了
required string url = 2;
optional string title = 3;
repeated string snippets = 4;
}
}