First let's look at a very simple example. Let's say you want to define a search request message format, where each search request has a query string, the particular page of results you are interested in, and a number of results per page. Here's the .proto file you use to define the message type.
首先让我们看一个非常简单的例子。比如说你想定义一个搜索请求的message格式,每一个搜索请求都有一个查询字符串,一个你所需结果的特定页码,和一个每页有多少条结果的数字。下面就是你用来定义message类型的.proto文件内容:
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
The first line of the file specifies that you're using
proto3
syntax: if you don't do this the protocol buffer compiler will assume you are using proto2. This must be the first non-empty, non-comment line of the file.
文件的第一行指定你所使用的是proto3
语法;如果你不指定,protocol buffer编译器会假定你所使用的是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.
SearchRequest
message 中指定了三个字段(名称/数值 对),每一个字段代表着你想要包含在该message类型中的一个数据块,每个字段都有名称和类型。
Specifying Field Types - 指定字段类型
In the above example, all the fields are scalar types: two integers (page_number
and result_per_page
) and a string (query
). However, you can also specify composite types for your fields, including enumerations and other message types.
在上边的例子中,所有的字段都是标量类型: 两个整型(page_number
and result_per_page
)和一个字符串类型(query
)。然而,你也可以为你的字段指定组合类型,包括枚举类型和其他message类型。
Assigning Field Numbers - 分配字段编号
As you can see, each field in the message definition has a unique number. These field numbers are used to identify your fields in the message binary format, and should not be changed once your message type is in use.
正如你所看到的,message中的每一个字段都有一个唯一编号。这些字段编号在message二进制格式中被用来识别你的字段,而且一旦你的message类型开始使用这些编号就不能被修改。
Note that field numbers in the range 1 through 15 take one byte to encode, including the field number and the field's type (you can find out more about this in Protocol Buffer Encoding). Field numbers in the range 16 through 2047 take two bytes. So you should reserve the numbers 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.
注意:编码时这些1到15的字段编号占用1字节,其中包括字段编号和字段类型(查看更多请参见Protocol Buffer编码)。从16到2047的字段编号占用2字节。因此你应该把编号1到15预留给经常出现的message元素。记得给以后可能添加的频繁出现的元素留出一些空间。
The smallest field number you can specify is 1, and the largest is 229 - 1, or 536,870,911. You also cannot use the numbers 19000 through 19999 (FieldDescriptor::kFirstReservedNumber
throughFieldDescriptor::kLastReservedNumber
), as they are reserved for the Protocol Buffers implementation—the protocol buffer compiler will complain if you use one of these reserved numbers in your .proto
. Similarly, you cannot use any previously reserved field numbers.
你指定的字段编号最小是1,最大是229 - 1,或536,870,911. 。你也不能使用19000到19999的编号(FieldDescriptor::kFirstReservedNumber
到FieldDescriptor::kLastReservedNumber
),因为了这些是为了实现Protocol Buffers而预留的 - 如果你在你的.proto
文件中使用这些预留编号,protocol buffer编译器会报错。你也不能使用任何先前预留的编号。
Specifying Field Rules - 指定字段规则
Message fields can be one of the following:
Message字段可以是下面的一种:
-
singular
: a well-formed message can have zero or one of this field (but not more than one). When using proto3 syntax, this is the default field rule when no other field rules are specified for a given field. You cannot determine whether it was parsed from the wire. It will be serialized to the wire unless it is the default value. For more on this subject, see Field Presence.
singular
: 一个符合语法规则的message可以有0或1个该字段(但是不能超过1个)。使用proto3语法时,一个给定字段没有指定其他字段规则时这个是默认字段规则。你不能确定是否从线路中解析了它。非默认值时该字段会被序列化到线路中。了解更多请参见Field Presence -
optional
: the same assingular
, except that you can check to see if the value was explicitly set. Anoptional
field is in one of two possible states:
optional
: 和singular
相同,区别在于你可以检查该字段值是否被明确设置过。一个optional
字段有以下两种可能状态:- the field is set, and contains a value that was explicitly set or parsed from the wire. It will be serialized to the wire.
字段被设置了值,包含了一个被明确设置的值或从线路中解析后的值。该字段会被序列化到线路中。 - the field is unset, and will return the default value. It will not be serialized to the wire.
字段值没有被设置,会返回默认值,该字段不会被序列化到线路中
- the field is set, and contains a value that was explicitly set or parsed from the wire. It will be serialized to the wire.
-
repeated
: this field type can be repeated zero or more times in a well-formed message. The order of the repeated values will be preserved.
repeated
:一个符合语法规则的message中该字段可以重复出现0次或多次。重复值的顺序会得到保留。 -
map
: this is a paired key/value field type. See Maps for more on this field type.
map
: 这是一种键值对字段类型。有关该字段想了解更多,请参见Maps
In proto3, repeated
fields of scalar numeric types use packed
encoding by default. You can find out more about packed
encoding in Protocol Buffer Encoding.
在proto3
中,标量数字类型的repeated
字段默认采用packed
编码方式。你可以在Protocol Buffer 编码章节中了解更多信息。
Adding More Message Types - 添加更多message类型
Multiple message types can be defined in a single .proto file. This is useful if you are defining multiple related messages – so, for example, if you wanted to define the reply message format that corresponds to your SearchResponse message type, you could add it to the same .proto:
多个message类型可以被定义在一个.proto文件中。这对于你要定义多个相关的message时很有用 - 所以,举个例子,如果你想定义一个回复message格式来对应于SearchRequest message类型,你可以将其添加到同一个.proto文件中:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
Adding Comments - 添加注释
To add comments to your .proto files, use C/C++-style // and /* ... / syntax.
在你.proto文件中添加注释,使用C/C++风格// 和/...*/语法。
/* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response.
*/
/* SearchRequest 代表了一次搜索查询,具有搜索选择项来表明响应中包含哪些结
* 果。
*/
message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want? - 你所需页码
int32 result_per_page = 3; // Number of results to return per page. - 每页的结果数量
}
Reserved Fields - 保留字段
If you update a message type by entirely removing a field, or commenting it out, future users can reuse the field number when making their own updates to the type. This can cause severe issues if they later load old versions of the same .proto
, including data corruption, privacy bugs, and so on. One way to make sure this doesn't happen is to specify that the field numbers (and/or names, which can also cause issues for JSON serialization) of your deleted fields are reserved
. The protocol buffer compiler will complain if any future users try to use these field identifiers.
如果通过删除整个字段来修改一个message类型,或者注视掉它,当后来的使用者修改该类型时可以重用该字段编号。如果他们后边加载同一.proto
文件的老版本时会导致很严重的问题,包括数据腐蚀、隐私错误等等。一种解决方案是将你需要删除的字段(和/或 名称,也会导致JSON序列化错误)标记为reversed
。如果后来的使用者尝试使用该保留字段编号或字段名称,protocol buffer 编译器会报错。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
Note that you can't mix field names and field numbers in the same reserved statement.
注意:你不可以将字段名称和字段编号放在同一预留声明语句中。
What's Generated From Your .proto
? - 你的.proto
都生成了什么(即通过protoc编译后的产物是什么)?
When you run the protocol buffer compiler on a .proto
, the compiler generates the code in your chosen language you'll need to work with the message types you've described in the file, including getting and setting field values, serializing your messages to an output stream, and parsing your messages from an input stream.
当你运行protocol buffer 编译器来编译.proto
文件时,编译器会生成你所选择的语言对应的代码,代码中会生成你接下来你需要使用到的在文件中描述的message类型,包括获取、设置字段值、序列化你的message到输出流以及从输入流解析你的消息
For C++, the compiler generates a
.h
and.cc
file from each.proto
, with a class for each message type described in your file.
对于C++,编译器会为每一个.proto
文件生成一个.h
和一个.cc
文件,其中中包含了你在.proto
文件中所描述的每一个message类型的类。For Java, the compiler generates a
.java
file with a class for each message type, as well as a specialBuilder
class for creating message class instances.
对于Java,编译器会生成一个.java
文件,该文件中包含了每一个message类型的类,和一个特殊Builder
用于创建message实例。For Kotlin, in addition to the Java generated code, the compiler generates a
.kt
file for each message type, containing a DSL which can be used to simplify creating message instances.
对于Kotlin,除了Java所生成的代码,编译器还为每一个message类型生成了一个.kt
文件,其中包含了一个DSL用于简化message实例创建。Python is a little different — the Python compiler generates a module with a static descriptor of each message type in your
.proto
, which is then used with a metaclass to create the necessary Python data access class at runtime.
Python有些不同 - Python编译器会生成一个模块,为.proto
文件中每一个message类型提供一个对应的静态描述器,该描述器运行时利用一个元类创建一个必要的Python数据获取类。For Go, the compiler generates a
.pb.go
file with a type for each message type in your file.
对于Go,编译器生成一个.pb.go
文件,其中为文件中的每一个message类型提供一个对应类型For Ruby, the compiler generates a
.rb
file with a Ruby module containing your message types.
对于Ruby,编译器会生成一个.rb
文件,其中有一个把包含你的message类型的Ruby模块For Objective-C, the compiler generates a
pbobjc.h
andpbobjc.m
file from each.proto
, with a class for each message type described in your file.
对于Objective-C,编译器会为每一个.proto
文件生成一个pbobjc.h
文件和一个pbobjc.m
文件,其中为你文件中描述的每一个message类型提供一个类。For C#, the compiler generates a
.cs
file from each.proto
, with a class for each message type described in your file.
对于C#,编译器会为每一个.proto
文件生成一个.cs
文件,其中为你文件中描述的每一个message类型提供一个类。For Dart, the compiler generates a
.pb.dart
file with a class for each message type in your file.
对于Dart,编译器会生成一个.pb.dart
文件,其中为你文件中的每一个message类型提供一个类。
You can find out more about using the APIs for each language by following the tutorial for your chosen language (proto3 versions coming soon). For even more API details, see the relevant API reference (proto3 versions also coming soon).
在接下来的教程中,你可以根据你所选择的语言(proto3很快上线)查看更多有关各个语言APIs如何使用的信息。关于更多API细节,参见有关API 参考(proto3版本也很快上线)