赞
踩
This topic covers how to use the version 3 of Protocol Buffers in your project.
本主题介绍如何在项目中使用协议缓冲区的版本3。
This guide describes how to use the protocol buffer language to structure your protocol buffer data, including .proto
file syntax and how to generate data access classes from your .proto
files. It covers the proto3 version of the protocol buffers language: for information on the proto2 syntax, see the Proto2 Language Guide.
本指南介绍了如何使用协议缓冲区语言来构建协议缓冲区数据,包括.proto文件语法,以及如何从.proto文件生成数据访问类。它涵盖了协议缓冲区的proto3版本:有关proto2语法的信息,请参阅proto2语言指南。
This is a reference guide – for a step by step example that uses many of the features described in this document, see the tutorial for your chosen language.
这是一个参考指南——有关使用本文档中描述的许多功能的逐步示例,请参阅所选语言的教程。
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.
首先我们来看一个非常简单的例子。假设您想要定义一个搜索请求消息格式,其中每个搜索请求都有一个查询字符串、感兴趣的特定页面的结果以及每页的多个结果。以下是用于定义消息类型的.proto文件。
- syntax = "proto3";
-
- message SearchRequest {
- string query = 1;
- int32 page_number = 2;
- int32 results_per_page = 3;
- }
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.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.In the earlier example, all the fields are scalar types: 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.
在前面的示例中,所有字段都是标量类型:两个整数(page_number和results_per_page)和一个字符串(query)。您还可以像为字段指定其他消息类型一样指定枚举和复合类型。
You must give each field in your message definition a number between 1
and 536,870,911
with the following restrictions:
必须为消息定义中的每个字段提供一个介于1和536870911之间的数字,但有以下限制:
19,000
to 19,999
are reserved for the Protocol Buffers implementation. The protocol buffer compiler will complain if you use one of these reserved field numbers in your message.This number cannot be changed once your message type is in use because it identifies the field in the message wire format. “Changing” a field number is equivalent to deleting that field and creating a new field with the same type but a new number. See Deleting Fields for how to do this properly.
一旦使用了消息类型,就不能更改此数字,因为它以消息有线格式标识字段。“更改”字段编号相当于删除该字段并创建一个具有相同类型但具有新编号的新字段。请参阅删除字段以了解如何正确执行此操作。
Field numbers should never be reused. Never take a field number out of the reserved list for reuse with a new field definition. See Consequences of Reusing Field Numbers.
字段号不应重复使用。永远不要将字段号从保留列表中删除,以便与新的字段定义一起重用。请参阅重复使用字段编号的后果。
You should use the field numbers 1 through 15 for the most-frequently-set fields. Lower field number values take less space in the wire format. For example, field numbers in the range 1 through 15 take one byte to encode. Field numbers in the range 16 through 2047 take two bytes. You can find out more about this in Protocol Buffer Encoding.
应该将字段编号1到15用于最频繁设置的字段。较低的字段编号值在导线格式中占用的空间较小。例如,范围为1到15的字段编号需要一个字节进行编码。16到2047范围内的字段编号占用两个字节。您可以在协议缓冲区编码中找到更多关于这方面的信息。
Reusing a field number makes decoding wire-format messages ambiguous.
重复使用字段号会使有线格式消息的解码变得不明确。
The protobuf wire format is lean and doesn’t provide a way to detect fields encoded using one definition and decoded using another.
protobuf-wire格式是精简的,并且没有提供一种方法来检测使用一种定义编码并使用另一种定义解码的字段。
Encoding a field using one definition and then decoding that same field with a different definition can lead to:
使用一个定义对字段进行编码,然后使用不同的定义对同一字段进行解码,可能会导致:
Common causes of field number reuse:
字段号重复使用的常见原因:
The max field is 29 bits instead of the more-typical 32 bits because three lower bits are used for the wire format. For more on this, see the Encoding topic.
最大字段是29位,而不是更典型的32位,因为有线格式使用三个较低的位。有关此方面的详细信息,请参阅编码主题。
Message fields can be one of the following:
消息字段可以是以下字段之一:
optional
: An optional
field is in one of two possible states:
可optional
:optional
字段处于两种可能状态之一:
You can check to see if the value was explicitly set.
可以检查该值是否已显式设置。
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:在格式良好的消息中,此字段类型可以重复零次或多次。重复值的顺序将被保留。
map
: this is a paired key/value field type. See Maps for more on this field type.
map:这是一个成对的键/值字段类型。有关此字段类型的详细信息,请参见贴图。
If no explicit field label is applied, the default field label, called “implicit field presence,” is assumed. (You cannot explicitly set a field to this state.) A well-formed message can have zero or one of this field (but not more than one). You also cannot determine whether a field of this type was parsed from the wire. An implicit presence field will be serialized to the wire unless it is the default value. For more on this subject, see Field Presence.
如果未应用显式字段标签,则假定为默认字段标签,称为“隐式字段存在”。(不能将字段显式设置为此状态。)格式正确的消息可以有零个或一个字段(但不能超过一个)。也无法确定是否从线中解析了此类型的字段。除非是默认值,否则隐式存在字段将被序列化到线。有关此主题的更多信息,请参阅现场演示。
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
编码。可以在协议缓冲区编码中找到有关packed
编码的更多信息。
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
:
在一个.proto文件中可以定义多个消息类型。如果正在定义多个相关消息,这很有用——例如,如果想定义与SearchResponse消息类型对应的回复消息格式,可以将其添加到同一个.proto:
- message SearchRequest {
- string query = 1;
- int32 page_number = 2;
- int32 results_per_page = 3;
- }
-
- message SearchResponse {
- ...
- }
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. */
-
- message SearchRequest {
- string query = 1;
- int32 page_number = 2; // Which page number do we want?
- int32 results_per_page = 3; // Number of results to return per page.
- }
Deleting fields can cause serious problems if not done properly.
如果操作不当,删除字段可能会导致严重问题。
When you no longer need a non-required field and all references have been deleted from client code, you may delete the field definition from the message. However, you must reserve the deleted field number. If you do not reserve the field number, it is possible for a developer to reuse that number in the future.
当不再需要非必填字段,并且所有引用都已从客户端代码中删除时,可以从消息中删除字段定义。但是,必须保留已删除的字段编号。如果不保留字段编号,开发人员将来可能会重用该编号。
You should also reserve the field name to allow JSON and TextFormat encodings of your message to continue to parse.
还应该保留字段名称,以允许消息的JSON和TextFormat编码继续解析。
If you update a message type by entirely deleting a field, or commenting it out, future developers can reuse the field number when making their own updates to the type. This can cause severe issues, as described in Consequences of Reusing Field Numbers.
如果通过完全删除字段或注释掉字段来更新消息类型,未来的开发人员可以在对该类型进行自己的更新时重用字段号。这可能会导致严重的问题,如重复使用字段编号的后果中所述。
To make sure this doesn’t happen, add your deleted field number to the reserved
list. To make sure JSON and TextFormat instances of your message can still be parsed, also add the deleted field name to a reserved
list.
要确保不会发生这种情况,请将已删除的字段编号添加到保留列表中。为了确保消息的JSON和TextFormat实例仍然可以解析,还可以将删除的字段名添加到保留列表中。
The protocol buffer compiler will complain if any future developers try to use these reserved field numbers or names.
如果将来的开发人员试图使用这些保留字段号或名称,协议缓冲区编译器会抱怨。
- message Foo {
- reserved 2, 15, 9 to 11;
- reserved "foo", "bar";
- }
Reserved field number ranges are inclusive (9 to 11
is the same as 9, 10, 11
). Note that you can’t mix field names and field numbers in the same reserved
statement.
保留字段编号范围包括在内(9至11与9、10、11相同)。请注意,不能在同一reserved
语句中混用字段名称和字段编号。
.proto
?.proto
产生了什么?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.
当你在.proto上运行协议缓冲区编译器时,编译器会用选择的语言生成代码,需要使用文件中描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,以及从输入流解析消息。
.h
and .cc
file from each .proto
, with a class for each message type described in your file..java
file with a class for each message type, as well as a special Builder
class for creating message class instances..kt
file for each message type, containing a DSL which can be used to simplify creating message instances..proto
, which is then used with a metaclass to create the necessary Python data access class at runtime..pb.go
file with a type for each message type in your file..rb
file with a Ruby module containing your message types.pbobjc.h
and pbobjc.m
file from each .proto
, with a class for each message type described in your file..cs
file from each .proto
, with a class for each message type described in your file..pb.dart
file with a class for each message type in your file.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版本即将推出)了解更多关于为每种语言使用API的信息。有关API的更多详细信息,请参阅相关的API参考(proto3版本也即将推出)。
A scalar message field can have one of the following types – the table shows the type specified in the .proto
file, and the corresponding type in the automatically generated class:
标量消息字段可以具有以下类型之一——该表显示了.proto文件中指定的类型以及自动生成的类中的相应类型:
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. 使用可变长度编码。对负数进行编码效率低下–如果的字段可能具有负值,请改用sint32。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. 使用可变长度编码。编码负数效率低下–如果的字段可能具有负值,请改用sint64。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
uint32 | Uses variable-length encoding. 使用可变长度编码。 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
uint64 | Uses variable-length encoding. 使用可变长度编码。 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. 使用可变长度编码。带符号的int值。这些比常规int32更有效地编码负数。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. 使用可变长度编码。带符号的int值。这些比常规int64更有效地编码负数。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. 总是四个字节。如果值经常大于228,则比uint32更有效。 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. 总是八个字节。如果值经常大于256,则比uint64更有效率。 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
sfixed32 | Always four bytes. 总是四个字节。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sfixed64 | Always eight bytes. 总是八个字节。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. 字符串必须始终包含UTF-8编码或7位ASCII文本,并且长度不能超过232。 | string | String | str/unicode[5] | string | String (UTF-8) | string | string | String |
bytes | May contain any arbitrary sequence of bytes no longer than 232. 可以包含长度不超过232的任意字节序列。 | string | ByteString | str (Python 2) bytes (Python 3) | []byte | String (ASCII-8BIT) | ByteString | string | List |
You can find out more about how these types are encoded when you serialize your message in Protocol Buffer Encoding.
当在协议缓冲区编码中序列化消息时,可以了解更多关于如何对这些类型进行编码的信息。
[1] Kotlin uses the corresponding types from Java, even for unsigned types, to ensure compatibility in mixed Java/Kotlin codebases.
[1] Kotlin使用Java中的相应类型,即使对于无符号类型也是如此,以确保混合Java/Kotlin代码库中的兼容性。
[2] In Java, unsigned 32-bit and 64-bit integers are represented using their signed counterparts, with the top bit simply being stored in the sign bit.
[2] 在Java中,无符号的32位和64位整数是用它们的有符号对应物来表示的,最高位只存储在符号位中。
[3] In all cases, setting values to a field will perform type checking to make sure it is valid.
[3] 在所有情况下,将值设置为字段将执行类型检查以确保其有效。
[4] 64-bit or unsigned 32-bit integers are always represented as long when decoded, but can be an int if an int is given when setting the field. In all cases, the value must fit in the type represented when set. See [2].
[4] 64位或无符号32位整数在解码时总是表示为长,但如果在设置字段时给定int,则可以是int。在任何情况下,该值都必须适合设置时表示的类型。参见[2]。
[5] Python strings are represented as unicode on decode but can be str if an ASCII string is given (this is subject to change).
[5] Python字符串在解码时表示为unicode,但如果给定ASCII字符串,则可以是str(这可能会更改)。
[6] Integer is used on 64-bit machines and string is used on 32-bit machines.
[6] 整数用于64位计算机,字符串用于32位计算机。
When a message is parsed, if the encoded message does not contain a particular singular element, the corresponding field in the parsed object is set to the default value for that field. These defaults are type-specific:
解析消息时,如果编码的消息不包含特定的单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:
The default value for repeated fields is empty (generally an empty list in the appropriate language).
重复字段的默认值为空(通常是相应语言的空列表)。
Note that for scalar message fields, once a message is parsed there’s no way of telling whether a field was explicitly set to the default value (for example whether a boolean was set to false
) or just not set at all: you should bear this in mind when defining your message types. For example, don’t have a boolean that switches on some behavior when set to false
if you don’t want that behavior to also happen by default. Also note that if a scalar message field is set to its default, the value will not be serialized on the wire.
请注意,对于标量消息字段,一旦解析了消息,就无法判断字段是显式设置为默认值(例如布尔值是否设置为false)还是根本没有设置:在定义消息类型时应记住这一点。例如,如果您不希望某些行为在默认情况下也发生,那么在设置为false时,不要有用于切换某些行为的布尔值。还要注意,如果标量消息字段设置为默认值,则该值将不会在连线上序列化。
See the generated code guide for your chosen language for more details about how defaults work in generated code.
有关默认值如何在生成代码中工作的更多详细信息,请参阅所选语言的生成代码指南。
When you’re defining a message type, you might want one of its fields to only have one of a pre-defined list of values. For example, let’s say you want to add a corpus
field for each SearchRequest
, where the corpus can be UNIVERSAL
, WEB
, IMAGES
, LOCAL
, NEWS
, PRODUCTS
or VIDEO
. You can do this very simply by adding an enum
to your message definition with a constant for each possible value.
定义消息类型时,可能希望其中一个字段仅具有预定义值列表中的一个值。例如,假设您想为每个SearchRequest添加一个corpus
字段,其中corpus
可以是UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS或VIDEO。只需在消息定义中添加一个枚举,并为每个可能的值添加一个常量,就可以做到这一点。
In the following example we’ve added an enum
called Corpus
with all the possible values, and a field of type Corpus
:
在下面的示例中,我们添加了一个名为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;
- }
As you can see, the Corpus
enum’s first constant maps to zero: every enum definition must contain a constant that maps to zero as its first element. This is because:
正如所看到的,Corpus枚举的第一个常量映射到零:每个枚举定义都必须包含一个映射到零的常量作为其第一个元素。这是因为:
You can define aliases by assigning the same value to different enum constants. To do this you need to set the allow_alias
option to true
, otherwise the protocol compiler generates a warning message when aliases are found. Though all alias values are valid during deserialization, the first value is always used when serializing.
可以通过将相同的值分配给不同的枚举常量来定义别名。要做到这一点,需要将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;
- }
Enumerator constants must be in the range of a 32-bit integer. Since enum
values use varint encoding on the wire, negative values are inefficient and thus not recommended. You can define enum
s within a message definition, as in the above example, or outside – these enum
s can be reused in any message definition in your .proto
file. You can also use an enum
type declared in one message as the type of a field in a different message, using the syntax _MessageType_._EnumType_
.
枚举器常量必须在32位整数的范围内。由于枚举值在线上使用可变编码,因此负值效率低下,因此不推荐使用。可以在消息定义内定义枚举,如上例所示,也可以在外部定义枚举-这些枚举可以在.proto文件中的任何消息定义中重用。还可以使用语法_MessageType_._EnumType_
将一条消息中声明的枚举类型用作另一条消息的字段类型。
When you run the protocol buffer compiler on a .proto
that uses an enum
, the generated code will have a corresponding enum
for Java, Kotlin, or C++, or a special EnumDescriptor
class for Python that’s used to create a set of symbolic constants with integer values in the runtime-generated class.
当在使用枚举的.proto上运行协议缓冲区编译器时,生成的代码将具有对应的Java、Kotlin或C++的枚举,或用于Python的特殊EnumDescriptor类,用于在运行时生成的类中创建一组具有整数值的符号常量。
Important
重点
The generated code may be subject to language-specific limitations on the number of enumerators (low thousands for one language). Review the limitations for the languages you plan to use.
生成的代码可能会受到特定语言的枚举器数量限制(一种语言的枚举数为数千)。查看计划使用的语言的限制。
During deserialization, unrecognized enum values will be preserved in the message, though how this is represented when the message is deserialized is language-dependent. In languages that support open enum types with values outside the range of specified symbols, such as C++ and Go, the unknown enum value is simply stored as its underlying integer representation. In languages with closed enum types such as Java, a case in the enum is used to represent an unrecognized value, and the underlying integer can be accessed with special accessors. In either case, if the message is serialized the unrecognized value will still be serialized with the message.
在反序列化过程中,无法识别的枚举值将保留在消息中,尽管反序列化消息时如何表示枚举值取决于语言。在支持值在指定符号范围之外的开放枚举类型的语言中,如C++和Go,未知枚举值仅存储为其基础整数表示形式。在Java等具有封闭枚举类型的语言中,枚举中的大小写用于表示无法识别的值,并且可以使用特殊的访问器访问底层整数。在任何一种情况下,如果消息被序列化,则无法识别的值仍将与消息一起序列化。
Important
重点
For information on how enums should work contrasted with how they currently work in different languages, see Enum Behavior.
有关枚举应如何工作以及它们当前在不同语言中的工作方式的信息,请参见枚举行为。
For more information about how to work with message enum
s in your applications, see the generated code guide for your chosen language.
有关如何在应用程序中使用消息枚举的更多信息,请参阅所选语言的生成代码指南。
If you update an enum type by entirely removing an enum entry, or commenting it out, future users can reuse the numeric value 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 numeric values (and/or names, which can also cause issues for JSON serialization) of your deleted entries are reserved
. The protocol buffer compiler will complain if any future users try to use these identifiers. You can specify that your reserved numeric value range goes up to the maximum possible value using the max
keyword.
如果通过完全删除枚举项或注释掉它来更新枚举类型,则未来的用户可以在对该类型进行自己的更新时重用该数值。如果他们以后加载相同.proto的旧版本,包括数据损坏、隐私漏洞等,这可能会导致严重问题。确保这种情况不会发生的一种方法是指定已删除条目的数值(和/或名称,这也可能导致JSON序列化问题)是保留的。如果将来有任何用户试图使用这些标识符,协议缓冲区编译器将发出抱怨。可以使用max关键字指定保留的数值范围最高可达可能的最大值。
- enum Foo {
- reserved 2, 15, 9 to 11, 40 to max;
- reserved "FOO", "BAR";
- }
Note that you can’t mix field names and numeric values in the same reserved
statement.
请注意,不能在同一个保留语句中混合使用字段名和数值。
You can use other message types as field types. For example, let’s say you wanted to include Result
messages in each SearchResponse
message – to do this, you can define a Result
message type in the same .proto
and then specify a field of type Result
in SearchResponse
:
可以使用其他消息类型作为字段类型。例如,假设希望在每个SearchResponse消息中包含Result消息——为此,可以在相同的.proto中定义Result消息类型,然后在SearchResponse中指定Result类型的字段:
- message SearchResponse {
- repeated Result results = 1;
- }
-
- message Result {
- string url = 1;
- string title = 2;
- repeated string snippets = 3;
- }
In the above example, the Result
message type is defined in the same file as SearchResponse
– what if the message type you want to use as a field type is already defined in another .proto
file?
在上面的示例中,Result消息类型与SearchResponse在同一个文件中定义——如果要用作字段类型的消息类型已经在另一个.proto文件中定义了,该怎么办?
You can use definitions from other .proto
files by importing them. To import another .proto
’s definitions, you add an import statement to the top of your file:
可以通过导入其他.proto文件中的定义来使用它们。要导入另一个.proto的定义,请在文件顶部添加一个import语句:
import "myproject/other_protos.proto";
By default, you can use definitions only from directly imported .proto
files. However, sometimes you may need to move a .proto
file to a new location. Instead of moving the .proto
file directly and updating all the call sites in a single change, you can put a placeholder .proto
file in the old location to forward all the imports to the new location using the import public
notion.
默认情况下,只能使用直接导入的.proto文件中的定义。但是,有时可能需要将.prote文件移动到新位置。可以在旧位置放置一个占位符.proto文件,使用导入公共概念将所有导入转发到新位置,而不是直接移动.proto文件并在一次更改中更新所有调用站点。
Note that the public import functionality is not available in Java.
请注意,公共导入功能在Java中不可用。
import public
dependencies can be transitively relied upon by any code importing the proto containing the import public
statement. For example:
导入公共依赖项可以通过导入包含导入公共语句的proto的任何代码来传递依赖。例如:
- // 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
The protocol compiler searches for imported files in a set of directories specified on the protocol compiler command line using the -I
/--proto_path
flag. If no flag was given, it looks in the directory in which the compiler was invoked. In general you should set the --proto_path
flag to the root of your project and use fully qualified names for all imports.
协议编译器使用-I/-proto_path标志在协议编译器命令行上指定的一组目录中搜索导入的文件。如果没有给出任何标志,它将在调用编译器的目录中查找。通常,应该将--proto_path标志设置为项目的根,并对所有导入使用完全限定的名称。
It’s possible to import proto2 message types and use them in your proto3 messages, and vice versa. However, proto2 enums cannot be used directly in proto3 syntax (it’s okay if an imported proto2 message uses them).
可以导入proto2消息类型并在proto3消息中使用它们,反之亦然。但是,proto2 enum不能直接在proto3语法中使用(如果导入的proto2消息使用它们也可以)。
You can define and use message types inside other message types, as in the following example – here the Result
message is defined inside the SearchResponse
message:
可以在其他消息类型中定义和使用消息类型,如以下示例所示——这里的Result消息是在SearchResponse消息中定义的:
- message SearchResponse {
- message Result {
- string url = 1;
- string title = 2;
- repeated string snippets = 3;
- }
- repeated Result results = 1;
- }
If you want to reuse this message type outside its parent message type, you refer to it as _Parent_._Type_
:
如果要在其父消息类型之外重用此消息类型,请将使用_Parent_._Type_
:
- message SomeOtherMessage {
- SearchResponse.Result result = 1;
- }
You can nest messages as deeply as you like:
可以任意深度地嵌套消息:
- message Outer { // Level 0
- message MiddleAA { // Level 1
- message Inner { // Level 2
- int64 ival = 1;
- bool booly = 2;
- }
- }
- message MiddleBB { // Level 1
- message Inner { // Level 2
- int32 ival = 1;
- bool booly = 2;
- }
- }
- }
If an existing message type no longer meets all your needs – for example, you’d like the message format to have an extra field – but you’d still like to use code created with the old format, don’t worry! It’s very simple to update message types without breaking any of your existing code when you use the binary wire format.
如果现有的消息类型不再满足所有需求——例如,希望消息格式有一个额外的字段——但仍然希望使用用旧格式创建的代码,不要担心!当使用二进制线格式时,在不破坏任何现有代码的情况下更新消息类型非常简单。
Note
注
If you use JSON or proto text format to store your protocol buffer messages, the changes that you can make in your proto definition are different.
如果使用JSON或proto文本格式来存储协议缓冲区消息,那么可以在proto定义中进行的更改是不同的。
Check Proto Best Practices and the following rules:
检查Proto最佳实践和以下规则:
.proto
can’t accidentally reuse the number.int32
, uint32
, int64
, uint64
, and bool
are all compatible – this means you can change a field from one of these types to another without breaking forwards- or backwards-compatibility. If a number is parsed from the wire which doesn’t fit in the corresponding type, you will get the same effect as if you had cast the number to that type in C++ (for example, if a 64-bit number is read as an int32, it will be truncated to 32 bits).sint32
and sint64
are compatible with each other but are not compatible with the other integer types.string
and bytes
are compatible as long as the bytes are valid UTF-8.bytes
if the bytes contain an encoded version of the message.fixed32
is compatible with sfixed32
, and fixed64
with sfixed64
.string
, bytes
, and message fields, optional
is compatible with repeated
. Given serialized data of a repeated field as input, clients that expect this field to be optional
will take the last input value if it’s a primitive type field or merge all input elements if it’s a message type field. Note that this is not generally safe for numeric types, including bools and enums. Repeated fields of numeric types can be serialized in the packed format, which will not be parsed correctly when an optional
field is expected.enum
is compatible with int32
, uint32
, int64
, and uint64
in terms of wire format (note that values will be truncated if they don’t fit). However be aware that client code may treat them differently when the message is deserialized: for example, unrecognized proto3 enum
types will be preserved in the message, but how this is represented when the message is deserialized is language-dependent. Int fields always just preserve their value.optional
field or extension into a member of a new oneof
is binary compatible, however for some languages (notably, Go) the generated code’s API will change in incompatible ways. For this reason, Google does not make such changes in its public APIs, as documented in AIP-180. With the same caveat about source-compatibility, moving multiple fields into a new oneof
may be safe if you are sure that no code sets more than one at a time. Moving fields into an existing oneof
is not safe. Likewise, changing a single field oneof
to an optional
field or extension is safe.optional
字段或扩展更改为新oneof
或扩展的成员是二进制兼容的,但对于某些语言(特别是Go),生成的代码的API将以不兼容的方式更改。由于这个原因,谷歌没有在其公共API中进行这样的更改,正如AIP-180中所记录的那样。关于源代码兼容性,同样需要注意的是,如果确信没有代码一次设置多个字段,那么将多个字段移动到一个新oneof
中可能是安全的。将字段移动到的现有字段中是不安全的。同样,将其中一个字段更改为optional
字段或o扩展
也是安全的。Unknown fields are well-formed protocol buffer serialized data representing fields that the parser does not recognize. For example, when an old binary parses data sent by a new binary with new fields, those new fields become unknown fields in the old binary.
未知字段是格式良好的协议缓冲区序列化数据,表示解析器无法识别的字段。例如,当一个旧二进制文件用新字段解析新二进制文件发送的数据时,这些新字段将成为旧二进制文件中的未知字段。
Originally, proto3 messages always discarded unknown fields during parsing, but in version 3.5 we reintroduced the preservation of unknown fields to match the proto2 behavior. In versions 3.5 and later, unknown fields are retained during parsing and included in the serialized output.
最初,proto3消息在解析过程中总是丢弃未知字段,但在3.5版本中,我们重新引入了未知字段的保留,以匹配proto2的行为。在3.5及更高版本中,解析过程中会保留未知字段,并将其包含在序列化输出中。
The Any
message type lets you use messages as embedded types without having their .proto definition. An Any
contains an arbitrary serialized message as bytes
, along with a URL that acts as a globally unique identifier for and resolves to that message’s type. To use the Any
type, you need to import google/protobuf/any.proto
.
Any消息类型允许将消息用作嵌入类型,而不需要它们的.proto定义。Any包含一个以字节形式的任意序列化消息,以及一个URL,该URL用作该消息的全局唯一标识符并解析为该消息的类型。要使用Any类型,需要导入google/protobuf/Any.proto。
- import "google/protobuf/any.proto";
-
- message ErrorStatus {
- string message = 1;
- repeated google.protobuf.Any details = 2;
- }
The default type URL for a given message type is type.googleapis.com/_packagename_._messagename_
.
给定消息类型的默认类型URL是type.googleapis.com/_packagename_._messagename_。
Different language implementations will support runtime library helpers to pack and unpack Any
values in a typesafe manner – for example, in Java, the Any
type will have special pack()
and unpack()
accessors, while in C++ there are PackFrom()
and UnpackTo()
methods:
不同的语言实现将支持运行库助手以类型安全的方式打包和解包Any值——例如,在Java中,Any类型将具有特殊的pack()和unpack(),而在C++中有PackFrom()和UnpackTo()方法:
- // Storing an arbitrary message type in Any.
- NetworkErrorDetails details = ...;
- ErrorStatus status;
- status.add_details()->PackFrom(details);
-
- // Reading an arbitrary message from Any.
- ErrorStatus status = ...;
- for (const google::protobuf::Any& detail : status.details()) {
- if (detail.Is<NetworkErrorDetails>()) {
- NetworkErrorDetails network_error;
- detail.UnpackTo(&network_error);
- ... processing network_error ...
- }
- }
Currently the runtime libraries for working with Any
types are under development.
目前,用于处理Any类型的运行库正在开发中。
If you are already familiar with proto2 syntax, the Any
can hold arbitrary proto3 messages, similar to proto2 messages which can allow extensions.
如果已经熟悉proto2语法,那么Any可以容纳任意的proto3消息,类似于可以允许扩展的proto2消息。
If you have a message with many fields and where at most one field will be set at the same time, you can enforce this behavior and save memory by using the oneof feature.
如果有一条包含多个字段的消息,并且同时最多设置一个字段,则可以使用oneof功能强制执行此行为并节省内存。
Oneof fields are like regular fields except all the fields in a oneof share memory, and at most one field can be set at the same time. Setting any member of the oneof automatically clears all the other members. You can check which value in a oneof is set (if any) using a special case()
or WhichOneof()
method, depending on your chosen language.
除了一个共享内存中的所有字段外,Oneof字段类似于常规字段,并且最多可以同时设置一个字段。设置其中一个的任何成员会自动清除所有其他成员。根据选择的语言,可以使用特殊的case()或WhichOneof()方法来检查其中一个值中的哪个值是设置的(如果有的话)。
Note that if multiple values are set, the last set value as determined by the order in the proto will overwrite all previous ones.
请注意,如果设置了多个值,则由proto中的顺序确定的最后一个设置值将覆盖之前的所有值。
使用Oneof
To define a oneof in your .proto
you use the oneof
keyword followed by your oneof name, in this case test_oneof
:
要在.proto中定义oneof,请使用oneof关键字,后跟oneof名称,在本例中为test_oneof:
- message SampleMessage {
- oneof test_oneof {
- string name = 4;
- SubMessage sub_message = 9;
- }
- }
You then add your oneof fields to the oneof definition. You can add fields of any type, except map
fields and repeated
fields.
然后将字段之一添加到定义字段之一。可以添加任何类型的字段,映射字段和重复字段除外。
In your generated code, oneof fields have the same getters and setters as regular fields. You also get a special method for checking which value (if any) in the oneof is set. You can find out more about the oneof API for your chosen language in the relevant API reference.
在生成的代码中,其中一个字段具有与常规字段相同的getter和setter。还可以获得一个特殊的方法来检查oneof中的哪个值(如果有的话)被设置。可以在相关的API参考资料中找到有关所选语言的API之一的更多信息。
Setting a oneof field will automatically clear all other members of the oneof. So if you set several oneof fields, only the last field you set will still have a value.
设置oneof字段将自动清除oneof的所有其他成员。因此,如果设置了几个字段,那么只有设置的最后一个字段仍有值。
- SampleMessage message;
- message.set_name("name");
- CHECK_EQ(message.name(), "name");
- // Calling mutable_sub_message() will clear the name field and will set
- // sub_message to a new instance of SubMessage with none of its fields set.
- message.mutable_sub_message();
- CHECK(message.name().empty());
If the parser encounters multiple members of the same oneof on the wire, only the last member seen is used in the parsed message.
如果解析器在线上遇到同一个的多个成员,则在解析的消息中只使用最后一个看到的成员。
A oneof cannot be repeated
.
oneof不能重复。
Reflection APIs work for oneof fields.
反射API适用于其中一个字段。
If you set a oneof field to the default value (such as setting an int32 oneof field to 0), the “case” of that oneof field will be set, and the value will be serialized on the wire.
如果将oneof字段设置为默认值(例如将int32 oneof字段设为0),则会设置该oneof字段的“case”,并在线上序列化该值。
If you’re using C++, make sure your code doesn’t cause memory crashes. The following sample code will crash because sub_message
was already deleted by calling the set_name()
method.
如果使用的是C++,请确保代码不会导致内存崩溃。以下示例代码将崩溃,因为已通过调用set_name()方法删除了子消息。
- SampleMessage message;
- SubMessage* sub_message = message.mutable_sub_message();
- message.set_name("name"); // Will delete sub_message
- sub_message->set_... // Crashes here
Again in C++, if you Swap()
two messages with oneofs, each message will end up with the other’s oneof case: in the example below, msg1
will have a sub_message
and msg2
will have a name
.
同样在C++中,如果用其中一个Swap()两条消息,每条消息都将以另一条消息的大小写结尾:在下面的示例中,msg1将有一个sub_message
,msg2将有一个name
。
- SampleMessage msg1;
- msg1.set_name("name");
- SampleMessage msg2;
- msg2.mutable_sub_message();
- msg1.swap(&msg2);
- CHECK(msg1.has_sub_message());
- CHECK_EQ(msg2.name(), "name");
Be careful when adding or removing oneof fields. If checking the value of a oneof returns None
/NOT_SET
, it could mean that the oneof has not been set or it has been set to a field in a different version of the oneof. There is no way to tell the difference, since there’s no way to know if an unknown field on the wire is a member of the oneof.
添加或删除oneof字段时要小心。如果检查oneof的值返回None/NOT_SET,则可能意味着尚未设置oneof,或者已将其设置为oneof的不同版本中的字段。没有办法区分这两者,因为没有办法知道线上的未知字段是否是其中之一的成员。
If you want to create an associative map as part of your data definition, protocol buffers provides a handy shortcut syntax:
如果要创建关联映射作为数据定义的一部分,协议缓冲区提供了一种方便的快捷语法:
map<key_type, value_type> map_field = N;
…where the key_type
can be any integral or string type (so, any scalar type except for floating point types and bytes
). Note that enum is not a valid key_type
. The value_type
can be any type except another map.
…其中key_type可以是任何整数或字符串类型(因此,除了浮点类型和字节之外的任何标量类型)。请注意,enum不是有效的key_type。value_type可以是除其他映射之外的任何类型。
So, for example, if you wanted to create a map of projects where each Project
message is associated with a string key, you could define it like this:
因此,例如,如果想创建一个项目映射,其中每个Project消息都与一个字符串键相关联,可以这样定义它:
map<string, Project> projects = 3;
repeated
..proto
, maps are sorted by key. Numeric keys are sorted numerically.The generated map API is currently available for all proto3 supported languages. You can find out more about the map API for your chosen language in the relevant API reference.
生成的映射API目前可用于所有proto3支持的语言。可以在相关的API参考中找到有关所选语言的映射API的更多信息。
The map syntax is equivalent to the following on the wire, so protocol buffers implementations that do not support maps can still handle your data:
映射语法等效于以下内容,因此不支持映射的协议缓冲区实现仍然可以处理数据:
- message MapFieldEntry {
- key_type key = 1;
- value_type value = 2;
- }
-
- repeated MapFieldEntry map_field = N;
Any protocol buffers implementation that supports maps must both produce and accept data that can be accepted by the above definition.
任何支持映射的协议缓冲区实现都必须生成并接受上述定义可以接受的数据。
You can add an optional package
specifier to a .proto
file to prevent name clashes between protocol message types.
可以将可选的包说明符添加到.proto文件中,以防止协议消息类型之间的名称冲突。
- package foo.bar;
- message Open { ... }
You can then use the package specifier when defining fields of your message type:
然后,可以在定义消息类型的字段时使用包说明符:
- message Foo {
- ...
- foo.bar.Open open = 1;
- ...
- }
The way a package specifier affects the generated code depends on your chosen language:
包说明符影响生成代码的方式取决于选择的语言:
Open
would be in the namespace foo::bar
.option java_package
in your .proto
file.option go_package
in your .proto
file.PB_
is prepended). For example, Open
would be in the namespace Foo::Bar
.option csharp_namespace
in your .proto
file. For example, Open
would be in the namespace Foo.Bar
.Type name resolution in the protocol buffer language works like C++: first the innermost scope is searched, then the next-innermost, and so on, with each package considered to be “inner” to its parent package. A leading ‘.’ (for example, .foo.bar.Baz
) means to start from the outermost scope instead.
协议缓冲区语言中的类型名称解析与C++类似:首先搜索最内部的作用域,然后搜索下一个最内部的范围,依此类推,每个包都被认为是其父包的“内部”。前面的“。”(例如,.foo.bar.Baz)的意思是从最外面的范围开始。
The protocol buffer compiler resolves all type names by parsing the imported .proto
files. The code generator for each language knows how to refer to each type in that language, even if it has different scoping rules.
协议缓冲区编译器通过解析导入的.proto文件来解析所有类型名称。每种语言的代码生成器都知道如何引用该语言中的每种类型,即使它有不同的作用域规则。
If you want to use your message types with an RPC (Remote Procedure Call) system, you can define an RPC service interface in a .proto
file and the protocol buffer compiler will generate service interface code and stubs in your chosen language. So, for example, if you want to define an RPC service with a method that takes your SearchRequest
and returns a SearchResponse
, you can define it in your .proto
file as follows:
如果想在RPC(远程过程调用)系统中使用消息类型,可以在.proto文件中定义RPC服务接口,协议缓冲区编译器将以您选择的语言生成服务接口代码和存根。因此,例如,如果想用一个接受SearchRequest并返回SearchResponse的方法来定义RPC服务,可以在.proto文件中定义它,如下所示:
- service SearchService {
- rpc Search(SearchRequest) returns (SearchResponse);
- }
The most straightforward RPC system to use with protocol buffers is gRPC: a language- and platform-neutral open source RPC system developed at Google. gRPC works particularly well with protocol buffers and lets you generate the relevant RPC code directly from your .proto
files using a special protocol buffer compiler plugin.
与协议缓冲区一起使用的最简单的RPC系统是gRPC:一个由谷歌开发的与语言和平台无关的开源RPC系统。gRPC与协议缓冲区配合得特别好,可以使用特殊的协议缓冲区编译器插件直接从.proto文件生成相关的RPC代码。
If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation. You can find out more about this in the Proto2 Language Guide.
如果不想使用gRPC,也可以在自己的RPC实现中使用协议缓冲区。可以在Proto2语言指南中找到更多关于这方面的信息。
There are also a number of ongoing third-party projects to develop RPC implementations for Protocol Buffers. For a list of links to projects we know about, see the third-party add-ons wiki page.
还有许多正在进行的第三方项目来开发协议缓冲区的RPC实现。有关我们所知项目的链接列表,请参阅第三方加载项wiki页面。
Proto3 supports a canonical encoding in JSON, making it easier to share data between systems. The encoding is described on a type-by-type basis in the table below.
Proto3支持JSON中的规范编码,使系统之间更容易共享数据。编码在下表中按类型进行了描述。
When parsing JSON-encoded data into a protocol buffer, if a value is missing or if its value is null
, it will be interpreted as the corresponding default value.
在将JSON编码的数据解析到协议缓冲区时,如果某个值丢失或其值为null,则会将其解释为相应的默认值。
When generating JSON-encoded output from a protocol buffer, if a protobuf field has the default value and if the field doesn’t support field presence, it will be omitted from the output by default. An implementation may provide options to include fields with default values in the output.
当从协议缓冲区生成JSON编码的输出时,如果protobuf字段具有默认值,并且该字段不支持字段存在,则默认情况下,它将从输出中省略。实现可以提供选项以在输出中包括具有默认值的字段。
A proto3 field that is defined with the optional
keyword supports field presence. Fields that have a value set and that support field presence always include the field value in the JSON-encoded output, even if it is the default value.
使用optional
关键字定义的proto3字段支持字段存在。具有值集并且支持字段存在的字段始终在JSON编码的输出中包含字段值,即使它是默认值。
proto3 | JSON | JSON example | Notes |
---|---|---|---|
message | object | {"fooBar": v, "g": null, ...} | Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the 生成JSON对象。消息字段名称被映射到lowerCamelCase并成为JSON对象键。如果指定了json_name字段选项,则指定的值将改为用作键。分析器接受lowerCamelCase名称(或json_name选项指定的名称)和原始proto字段名称。null是所有字段类型的可接受值,并被视为相应字段类型的默认值。但是,不能将null用于json_name值。有关原因的更多信息,请参阅json_name的更严格验证。 |
enum | string | "FOO_BAR" | The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values. 使用proto中指定的枚举值的名称。分析器接受枚举名称和整数值。 |
map<K,V> | object | {"k": v, ...} | All keys are converted to strings. 所有键都转换为字符串。 |
repeated V | array | [v, ...] |
null被接受为空列表[]。 |
bool | true, false | true, false | |
string | string | "Hello World!" | |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" | JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted. JSON值将是使用带填充的标准base64编码作为字符串编码的数据。接受带/不带填充的标准或URL安全base64编码。 |
int32, fixed32, uint32 | number | 1, -10, 0 | JSON value will be a decimal number. Either numbers or strings are accepted. JSON值将是一个十进制数字。可以接受数字或字符串。 |
int64, fixed64, uint64 | string | "1", "-10" | JSON value will be a decimal string. Either numbers or strings are accepted. JSON值将是一个十进制字符串。可以接受数字或字符串。 |
float, double | number | 1.1, -10.0, 0, "NaN", "Infinity" | JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". Either numbers or strings are accepted. Exponent notation is also accepted. -0 is considered equivalent to 0. JSON值将是一个数字或特殊字符串值“NaN”、“Infinity”和“-Infinity”之一。可以接受数字或字符串。指数表示法也是可以接受的-0被认为等同于0。 |
Any | object | {"@type": "url", "f": v, ... } | If the 如果Any包含一个具有特殊JSON映射的值,它将被转换如下:{“@type”:xxx,“value”:yyy}。否则,该值将被转换为JSON对象,并插入“@type”字段以指示实际的数据类型。 |
Timestamp | string | "1972-01-01T10:00:20.021Z" | Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than "Z" are also accepted. 使用RFC 3339,其中生成的输出将始终Z归一化,并使用0、3、6或9个小数位数。除“Z”以外的偏移也可接受。 |
Duration | string | "1.000340012s", "1s" | Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix "s". Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix "s" is required. 生成的输出始终包含0、3、6或9个小数位数,具体取决于所需的精度,后跟后缀“s”。接受任何小数位数(也可以是零),只要它们符合纳秒精度,并且需要后缀“s”。 |
Struct | object | { ... } | Any JSON object. See 任何JSON对象。请参阅struct.proto。 |
Wrapper types | various types | 2, "2", "foo", true, "true", null, 0, ... | Wrappers use the same representation in JSON as the wrapped primitive type, except that 包装器在JSON中使用与包装的基元类型相同的表示,只是在数据转换和传输过程中允许并保留null。 |
FieldMask | string | "f.fooBar,h" | See 请参见field_mask.proto。 |
ListValue | array | [foo, bar, ...] | |
Value | value | Any JSON value. Check google.protobuf.Value for details. 任何JSON值。有关详细信息,请查看google.protobuf.Value。 | |
NullValue | null | JSON null | |
Empty | object | {} | An empty JSON object 一个空的JSON对象 |
A proto3 JSON implementation may provide the following options:
proto3 JSON实现可以提供以下选项:
Individual declarations in a .proto
file can be annotated with a number of options. Options do not change the overall meaning of a declaration, but may affect the way it is handled in a particular context. The complete list of available options is defined in /google/protobuf/descriptor.proto.
proto文件中的各个声明可以用许多选项进行注释。选项不会改变声明的总体含义,但可能会影响在特定上下文中处理声明的方式。可用选项的完整列表在/google/protobuf/descriptor.proto中定义。
Some options are file-level options, meaning they should be written at the top-level scope, not inside any message, enum, or service definition. Some options are message-level options, meaning they should be written inside message definitions. Some options are field-level options, meaning they should be written inside field definitions. Options can also be written on enum types, enum values, oneof fields, service types, and service methods; however, no useful options currently exist for any of these.
有些选项是文件级选项,这意味着它们应该在顶级范围内编写,而不是在任何消息、枚举或服务定义内。有些选项是消息级别的选项,这意味着它们应该写在消息定义中。有些选项是字段级选项,这意味着它们应该写在字段定义中。选项也可以写在枚举类型、枚举值、oneof类型、服务类型和服务方法上;然而,目前还没有任何有用的选择。
Here are a few of the most commonly used options:
以下是一些最常用的选项:
java_package
(file option): The package you want to use for your generated Java/Kotlin classes. If no explicit java_package
option is given in the .proto
file, then by default the proto package (specified using the “package” keyword in the .proto
file) will be used. However, proto packages generally do not make good Java packages since proto packages are not expected to start with reverse domain names. If not generating Java or Kotlin code, this option has no effect.
java_package(文件选项):要用于生成的java/Kotlin类的包。如果.proto文件中没有给出明确的java_package选项,那么默认情况下将使用proto包(使用.proto中的“package”关键字指定)。然而,proto包通常不是好的Java包,因为proto包不应该以反向域名开头。如果不生成Java或Kotlin代码,则此选项无效。
option java_package = "com.example.foo";
java_outer_classname
(file option): The class name (and hence the file name) for the wrapper Java class you want to generate. If no explicit java_outer_classname
is specified in the .proto
file, the class name will be constructed by converting the .proto
file name to camel-case (so foo_bar.proto
becomes FooBar.java
). If the java_multiple_files
option is disabled, then all other classes/enums/etc. generated for the .proto
file will be generated within this outer wrapper Java class as nested classes/enums/etc. If not generating Java code, this option has no effect.
java_outer_classname(文件选项):您想要生成的包装java类的类名(以及文件名)。如果.proto文件中没有指定显式的java_outer_classname,那么类名将通过将.proto的文件名转换为camel大小写来构造(因此foo_bar.proto变成FooBar.java)。如果禁用了java_multiple_files选项,则所有其他类/enums/等。为.proto文件生成的将在这个外部包装Java类中作为嵌套类/enums/等生成。如果不生成Java代码,则此选项无效。
option java_outer_classname = "Ponycopter";
java_multiple_files
(file option): If false, only a single .java
file will be generated for this .proto
file, and all the Java classes/enums/etc. generated for the top-level messages, services, and enumerations will be nested inside of an outer class (see java_outer_classname
). If true, separate .java
files will be generated for each of the Java classes/enums/etc. generated for the top-level messages, services, and enumerations, and the wrapper Java class generated for this .proto
file won’t contain any nested classes/enums/etc. This is a Boolean option which defaults to false
. If not generating Java code, this option has no effect.
java_multiple_files(file选项):如果为false,则只会为这个.proto文件生成一个.java文件,以及所有java类/enums/等。为顶级消息、服务和枚举生成的将嵌套在外部类中(请参阅java_outer_classname)。如果为true,则将为每个java类/enums/等生成单独的.java文件。为顶级消息、服务和枚举生成的,并且为该.proto文件生成的包装Java类将不包含任何嵌套类/enums/等。这是一个布尔选项,默认为false。如果不生成Java代码,则此选项无效。
option java_multiple_files = true;
optimize_for
(file option): Can be set to SPEED
, CODE_SIZE
, or LITE_RUNTIME
. This affects the C++ and Java code generators (and possibly third-party generators) in the following ways:
optimize_for(文件选项):可以设置为SPEED、CODE_SIZE或LITE_RUNTIME。这会以以下方式影响C++和Java代码生成器(可能还有第三方生成器):
SPEED
(default): The protocol buffer compiler will generate code for serializing, parsing, and performing other common operations on your message types. This code is highly optimized.CODE_SIZE
: The protocol buffer compiler will generate minimal classes and will rely on shared, reflection-based code to implement serialialization, parsing, and various other operations. The generated code will thus be much smaller than with SPEED
, but operations will be slower. Classes will still implement exactly the same public API as they do in SPEED
mode. This mode is most useful in apps that contain a very large number of .proto
files and do not need all of them to be blindingly fast.LITE_RUNTIME
: The protocol buffer compiler will generate classes that depend only on the “lite” runtime library (libprotobuf-lite
instead of libprotobuf
). The lite runtime is much smaller than the full library (around an order of magnitude smaller) but omits certain features like descriptors and reflection. This is particularly useful for apps running on constrained platforms like mobile phones. The compiler will still generate fast implementations of all methods as it does in SPEED
mode. Generated classes will only implement the MessageLite
interface in each language, which provides only a subset of the methods of the full Message
interface.option optimize_for = CODE_SIZE;
cc_enable_arenas
(file option): Enables arena allocation for C++ generated code.
cc_enable_arenas(文件选项):为C++生成的代码启用竞技场分配。
objc_class_prefix
(file option): Sets the Objective-C class prefix which is prepended to all Objective-C generated classes and enums from this .proto. There is no default. You should use prefixes that are between 3-5 uppercase characters as recommended by Apple. Note that all 2 letter prefixes are reserved by Apple.
objc_class_prefix(文件选项):设置Objective-C类前缀,该前缀预先附加到this.proto中所有Objective-C生成的类和枚举。没有默认值。应该按照苹果公司的建议使用3-5个大写字符之间的前缀。请注意,所有2个字母的前缀均由苹果公司保留。
deprecated
(field option): If set to true
, indicates that the field is deprecated and should not be used by new code. In most languages this has no actual effect. In Java, this becomes a @Deprecated
annotation. For C++, clang-tidy will generate warnings whenever deprecated fields are used. In the future, other language-specific code generators may generate deprecation annotations on the field’s accessors, which will in turn cause a warning to be emitted when compiling code which attempts to use the field. If the field is not used by anyone and you want to prevent new users from using it, consider replacing the field declaration with a reserved statement.
deprecated
(字段选项):如果设置为true,则表示该字段已弃用,新代码不应使用该字段。在大多数语言中,这并没有实际效果。在Java中,这变成了一个@Deprecated注释。对于C++,无论何时使用不推荐使用的字段,clang-tidy都会生成警告。将来,其他特定于语言的代码生成器可能会在字段的访问器上生成弃用注释,这反过来会导致在编译试图使用该字段的代码时发出警告。如果该字段没有被任何人使用,并且希望防止新用户使用它,请考虑用保留语句替换字段声明。
int32 old_field = 6 [deprecated = true];
Enum value options are supported. You can use the deprecated
option to indicate that a value shouldn’t be used anymore. You can also create custom options using extensions.
支持枚举值选项。可以使用deprecated
选项来指示不应该再使用某个值。也可以使用扩展创建自定义选项。
The following example shows the syntax for adding these options:
以下示例显示了添加这些选项的语法:
- import "google/protobufs/descriptor.proto";
-
- extend google.protobufs.EnumValueOptions {
- optional string string_name = 123456789;
- }
-
- enum Data {
- DATA_UNKNOWN = 0;
- DATA_SEARCH = 1 [deprecated = true];
- DATA_DISPLAY = 2 [
- (string_name) = "display_value"
- ]
- }
Continue to the next section, Custom Options to see how to apply custom options to enum values and to fields.
继续下一节“自定义选项”,了解如何将自定义选项应用于枚举值和字段。
Protocol Buffers also allows you to define and use your own options. This is an advanced feature which most people don’t need. If you do think you need to create your own options, see the Proto2 Language Guide for details. Note that creating custom options uses extensions, which are permitted only for custom options in proto3.
协议缓冲区还允许您定义和使用自己的选项。这是一个大多数人不需要的高级功能。如果确实认为需要创建自己的选项,请参阅Proto2语言指南了解详细信息。请注意,创建自定义选项使用扩展,这仅适用于proto3中的自定义选项。
Options have a notion of retention, which controls whether an option is retained in the generated code. Options have runtime retention by default, meaning that they are retained in the generated code and are thus visible at runtime in the generated descriptor pool. However, you can set retention = RETENTION_SOURCE
to specify that an option (or field within an option) must not be retained at runtime. This is called source retention.
选项有一个保留的概念,它控制一个选项是否保留在生成的代码中。默认情况下,选项具有运行时保留,这意味着它们保留在生成的代码中,因此在运行时在生成的描述符池中可见。但是,可以将retention=RETENTION_SOURCE
设置为指定在运行时不得保留选项(或选项中的字段)。这称为源保留。
Option retention is an advanced feature that most users should not need to worry about, but it can be useful if you would like to use certain options without paying the code size cost of retaining them in your binaries. Options with source retention are still visible to protoc
and protoc
plugins, so code generators can use them to customize their behavior.
选项保留是一项高级功能,大多数用户不必担心,但如果想使用某些选项,而不需要支付将其保留在二进制文件中的代码大小成本,那么它可能会很有用。保留源代码的选项对protoc和protoc插件仍然可见,因此代码生成器可以使用它们来自定义其行为。
Retention can be set directly on an option, like this:
可以直接在选项上设置保留,如下所示:
- extend google.protobuf.FileOptions {
- optional int32 source_retention_option = 1234
- [retention = RETENTION_SOURCE];
- }
It can also be set on a plain field, in which case it takes effect only when that field appears inside an option:
它也可以设置在普通字段上,在这种情况下,只有当该字段出现在选项中时,它才会生效:
- message OptionsMessage {
- int32 source_retention_field = 1 [retention = RETENTION_SOURCE];
- }
You can set retention = RETENTION_RUNTIME
if you like, but this has no effect since it is the default behavior. When a message field is marked RETENTION_SOURCE
, its entire contents are dropped; fields inside it cannot override that by trying to set RETENTION_RUNTIME
.
如果愿意,可以设置retention = RETENTION_RUNTIME
,但这没有任何效果,因为这是默认行为。当一个消息字段被标记为RETENTION_SOURCE时,其全部内容将被删除;它内部的字段无法通过尝试设置RETENTION_RUNTIME来覆盖它。
Note
注
As of Protocol Buffers 22.0, support for option retention is still in progress and only C++ and Java are supported. Go has support starting from 1.29.0. Python support is complete but has not made it into a release yet.
从协议缓冲区22.0开始,对选项保留的支持仍在进行中,仅支持C++和Java。Go支持从1.29.0开始。Python支持已经完成,但尚未发布。
Fields have a targets
option which controls the types of entities that the field may apply to when used as an option. For example, if a field has targets = TARGET_TYPE_MESSAGE
then that field cannot be set in a custom option on an enum (or any other non-message entity). Protoc enforces this and will raise an error if there is a violation of the target constraints.
字段有一个目标选项,用于控制字段用作选项时可能应用的实体类型。例如,如果一个字段的targets=TARGET_TYPE_MESSAGE,则不能在枚举(或任何其他非消息实体)的自定义选项中设置该字段。Protoc强制执行这一点,如果违反了目标约束,则会引发错误。
At first glance, this feature may seem unnecessary given that every custom option is an extension of the options message for a specific entity, which already constrains the option to that one entity. However, option targets are useful in the case where you have a shared options message applied to multiple entity types and you want to control the usage of individual fields in that message. For example:
乍一看,这一功能似乎没有必要,因为每个自定义选项都是特定实体的选项消息的扩展,这已经将选项限制在该实体上。但是,如果将共享选项消息应用于多个实体类型,并且希望控制该消息中各个字段的使用,则选项目标非常有用。例如:
- message MyOptions {
- string file_only_option = 1 [targets = TARGET_TYPE_FILE];
- int32 message_and_enum_option = 2 [targets = TARGET_TYPE_MESSAGE,
- targets = TARGET_TYPE_ENUM];
- }
-
- extend google.protobuf.FileOptions {
- optional MyOptions file_options = 50000;
- }
-
- extend google.protobuf.MessageOptions {
- optional MyOptions message_options = 50000;
- }
-
- extend google.protobuf.EnumOptions {
- optional MyOptions enum_options = 50000;
- }
-
- // OK: this field is allowed on file options
- option (file_options).file_only_option = "abc";
-
- message MyMessage {
- // OK: this field is allowed on both message and enum options
- option (message_options).message_and_enum_option = 42;
- }
-
- enum MyEnum {
- MY_ENUM_UNSPECIFIED = 0;
- // Error: file_only_option cannot be set on an enum.
- option (enum_options).file_only_option = "xyz";
- }
To generate the Java, Kotlin, Python, C++, Go, Ruby, Objective-C, or C# code you need to work with the message types defined in a .proto
file, you need to run the protocol buffer compiler protoc
on the .proto
. If you haven’t installed the compiler, download the package and follow the instructions in the README. For Go, you also need to install a special code generator plugin for the compiler: you can find this and installation instructions in the golang/protobuf repository on GitHub.
要生成Java、Kotlin、Python、C++、Go、Ruby、Objective-C或C#代码,需要使用.proto文件中定义的消息类型,需要在.proto上运行协议缓冲区编译器protoc。如果还没有安装编译器,请下载包并按照README中的说明进行操作。对于Go,还需要为编译器安装一个特殊的代码生成器插件:可以在GitHub上的golang/protobuf存储库中找到此插件和安装说明。
The Protocol Compiler is invoked as follows:
协议编译器的调用方式如下:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
IMPORT_PATH
specifies a directory in which to look for .proto
files when resolving import
directives. If omitted, the current directory is used. Multiple import directories can be specified by passing the --proto_path
option multiple times; they will be searched in order. -I=_IMPORT_PATH_
can be used as a short form of --proto_path
.
IMPORT_PATH指定解析导入指令时要在其中查找.proto文件的目录。如果省略,则使用当前目录。通过多次传递--proto_path选项,可以指定多个导入目录;它们将按顺序进行搜索。-I=_IMPORT_PATH_可以用作--proto_path
的缩写。
You can provide one or more output directives:
可以提供一个或多个输出指令:
--cpp_out
generates C++ code in DST_DIR
. See the C++ generated code reference for more.--java_out
generates Java code in DST_DIR
. See the Java generated code reference for more.--kotlin_out
generates additional Kotlin code in DST_DIR
. See the Kotlin generated code reference for more.--python_out
generates Python code in DST_DIR
. See the Python generated code reference for more.--go_out
generates Go code in DST_DIR
. See the Go generated code reference for more.--ruby_out
generates Ruby code in DST_DIR
. See the Ruby generated code reference for more.--objc_out
generates Objective-C code in DST_DIR
. See the Objective-C generated code reference for more.--csharp_out
generates C# code in DST_DIR
. See the C# generated code reference for more.--php_out
generates PHP code in DST_DIR
. See the PHP generated code reference for more.As an extra convenience, if the DST_DIR
ends in .zip
or .jar
, the compiler will write the output to a single ZIP-format archive file with the given name. .jar
outputs will also be given a manifest file as required by the Java JAR specification. Note that if the output archive already exists, it will be overwritten; the compiler is not smart enough to add files to an existing archive.
作为一种额外的便利,如果DST_DIR以.zip或.jar结尾,编译器将把输出写入一个具有给定名称的zip格式归档文件。jar输出还将根据Java jar规范的要求提供一个清单文件。请注意,如果输出档案已经存在,它将被覆盖;编译器不够智能,无法将文件添加到现有存档中。
You must provide one or more .proto
files as input. Multiple .proto
files can be specified at once. Although the files are named relative to the current directory, each file must reside in one of the IMPORT_PATH
s so that the compiler can determine its canonical name.
必须提供一个或多个.proto文件作为输入。可以同时指定多个.proto文件。尽管文件是相对于当前目录命名的,但每个文件都必须位于其中一个IMPORT_PATH中,以便编译器可以确定其规范名称。
Prefer not to put .proto
files in the same directory as other language sources. Consider creating a subpackage proto
for .proto
files, under the root package for your project.
不要将.proto文件与其他语言源放在同一目录中。考虑在项目的根包下为.proto文件创建一个子包proto。
When working with Java code, it’s handy to put related .proto
files in the same directory as the Java source. However, if any non-Java code ever uses the same protos, the path prefix will no longer make sense. So in general, put the protos in a related language-agnostic directory such as //myteam/mypackage
.
当使用Java代码时,将相关的.proto文件放在与Java源代码相同的目录中很方便。然而,如果任何非Java代码使用相同的protos,那么路径前缀将不再有意义。因此,一般来说,将protos放在相关的语言不可知目录中,例如//myteam/mypackage。
The exception to this rule is when it’s clear that the protos will be used only in a Java context, such as for testing.
这个规则的例外是,protos显然只在Java上下文中使用,例如用于测试。
For information about:
有关以下方面的信息:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。