Protobuf
# 概述
本文根据官方文档,整理而成。
# 示例
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_num = 2;
int32 results_per_page = 3;
}
2
3
4
5
6
7
- 第一行需声明proto版本,如果不声明,默认当proto2处理。
- The SearchRequest message definition specifies three fields (name/value pairs), one for each piece of data that you want to include in this type of message. Each field has a name and a type.
# 指定字段类型
在之前的例子中, 它们的字段都是单一类型: two integers (page_number and results_per_page) and a string (query). You can also specify enumerations and composite types like other message types for your field.
# 分配字段编号
You must give each field in your message definition a number between 1 and 536,870,911 with the following restrictions:
- 给定的数字在该消息的所有字段中必须是唯一的.
- 字段号19,000到19999是为协议缓冲区实现保留的。如果您在消息中使用这些保留字段号之一,协议缓冲区编译器将报错。
一旦使用了消息类型,就不能更改此号码,因为它标识了消息连接格式中的字段。
# 重复使用字段编号的后果
Reusing a field number makes decoding wire-format messages ambiguous.
# 指定字段标签
- optional:可选字段处于两种可能状态之一。
- repeated:此字段类型可以在格式良好的消息中重复0次或多次。重复值的顺序将被保留。(数组)
- map:this is a paired key/value field type.
- 如果未显示指定标签,则使用默认标签。在 proto3 语法中,如果不指定修饰类型,默认值为 singular. singular: 表示被修饰的字段最多出现 1次,即出现 0次或 1次。
# 添加更多的消息类型
可以在单个.proto文件中定义多个消息类型。这在定义多个相关消息时非常有用,例如,如果希望定义与SearchRequest消息类型对应的回复消息格式,则可以将其添加到相同的.proto中。
syntax="proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
message SearchResponse {
...
}
2
3
4
5
6
7
8
9
10
11
# 添加注解
To add comments to your .proto files, use C/C++-style // and /* ... */ syntax.
# 删除字段
如果操作不当,删除字段可能会导致严重的问题。
# Reserved字段
如果您通过完全删除一个字段或将其注释掉来更新消息类型,那么将来的开发人员可以在对该类型进行更新时重用该字段号。这可能导致严重的问题,如重用字段号的后果中所述。 为了确保不会发生这种情况,请将已删除的字段号添加到保留列表中。为了确保消息的JSON和TextFormat实例仍然可以被解析,还要将删除的字段名称添加到保留列表中。 如果将来的开发人员试图使用这些保留的字段号或名称,协议缓冲区编译器将会报错。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
2
3
4
保留字段的取值范围是包含的(9 ~ 11与9、10、11相同)。注意,不能在同一保留语句中混合使用字段名和字段号。
# 值类型
.proto Type | Java Type | Go Type | PHP Type |
---|---|---|---|
double | double | float64 | float |
float | float | float32 | float |
int32 | int | int32 | integer |
int64 | long | int64 | integer |
uint32 | int | uint32 | integer |
uint64 | long | uint64 | integer |
bool | boolean | bool | boolean |
string | string | string | string |
bytes | ByteString | []byte | string |
# 默认值
- string 默认值为空字符串
- bytes 默认值为空的字节数组
- bool 默认值为false
- 数值类型 默认值为0
- For enums, the default value is the first defined enum value, which must be 0.
# 枚举类型
在定义消息类型时,您可能希望其中一个字段只具有预定义值列表中的一个。例如,假设您想为每个SearchRequest添加一个语料库字段,其中语料库可以是UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS或VIDEO。只需在消息定义中添加一个枚举,并为每个可能的值添加一个常量,就可以非常简单地做到这一点。 在下面的示例中,我们添加了一个名为Corpus的枚举,其中包含所有可能的值,以及一个类型为Corpus的字段:
enum Corpus {
CORPUS_UNSPECIFIED = 0;
CORPUS_UNIVERSAL = 1;
CORPUS_WEB = 2;
CORPUS_IMAGES = 3;
CORPUS_LOCAL = 4;
CORPUS_NEWS = 5;
CORPUS_PRODUCTS = 6;
CORPUS_VIDEO = 7;
}
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
Corpus corpus = 4;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
正如你所看到的,Corpus枚举的第一个常量映射到0:每个枚举定义必须包含一个映射到0的常量作为其第一个元素。这是因为:
- 必须有一个零值,以便我们可以使用0作为数字默认值。
- 为了与proto2语义兼容,零值需要是第一个元素,在proto2语义中,第一个枚举值总是默认值。
可以通过将相同的值赋给不同的枚举常量来定义别名。为此,您需要将allow_alias选项设置为true,否则,当找到别名时,协议编译器会生成一条警告消息。虽然所有别名值在反序列化期间都是有效的,但序列化时总是使用第一个值。
enum EnumAllowingAlias {
option allow_alias = true;
EAA_UNSPECIFIED = 0;
EAA_STARTED = 1;
EAA_RUNNING = 1;
EAA_FINISHED = 2;
}
enum EnumNotAllowingAlias {
ENAA_UNSPECIFIED = 0;
ENAA_STARTED = 1;
// ENAA_RUNNING = 1; // Uncommenting this line will cause a warning message.
ENAA_FINISHED = 2;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Enumerator constants must be in the range of a 32-bit integer.
# 使用其他消息类型
您可以使用其他消息类型作为字段类型。例如,假设您希望在每个SearchResponse消息中包含Result消息,那么您可以在相同的.proto中定义Result消息类型,然后在SearchResponse中指定Result类型的字段.
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
2
3
4
5
6
7
8
9
# Importing Definitions
import "myproject/other_protos.proto";
# 嵌套类型
您可以在其他消息类型中定义和使用消息类型,如下面的示例所示,Result消息在SearchResponse消息中定义。
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
2
3
4
5
6
7
8
# Any类型
要使用Any类型,您需要导入Google/protobuf/any.proto。
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
2
3
4
5
6
# Packages
您可以向.proto文件添加一个package说明符,以防止协议消息类型之间的名称冲突。
package foo.bar;
message Open { ... }
2
然后,您可以在定义消息类型的字段时使用package说明符.
message Foo {
...
foo.bar.Open open = 1;
...
}
2
3
4
5
每种编程语言中,package说明符是不同的。
- In Java and Kotlin, the package is used as the Java package, unless you explicitly provide an option java_package in your .proto file.
- In Go, the package is used as the Go package name, unless you explicitly provide an option go_package in your .proto file.