当前位置:   article > 正文

protobuf:语言指南(proto 3)_proto3

proto3

Language Guide (proto 3)

语言指南(proto 3)

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.

​这是一个参考指南——有关使用本文档中描述的许多功能的逐步示例,请参阅所选语言的教程。

Defining A Message Type

定义消息类型

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文件。

  1. syntax = "proto3";
  2. message SearchRequest {
  3. string query = 1;
  4. int32 page_number = 2;
  5. int32 results_per_page = 3;
  6. }
  • 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语法:如果不这样做,协议缓冲区编译器将假设正在使用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消息定义指定了三个字段(名称/值对),每个字段对应要包含在此类消息中的每一条数据。每个字段都有一个名称和一个类型。

Specifying Field Types

指定字段类型

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)。您还可以像为字段指定其他消息类型一样指定枚举和复合类型。

Assigning Field Numbers

指定字段编号

You must give each field in your message definition a number between 1 and 536,870,911 with the following restrictions:

必须为消息定义中的每个字段提供一个介于1和536870911之间的数字,但有以下限制:

  • The given number must be unique among all fields for that message.
  • 给定的数字在该消息的所有字段中必须是唯一的。
  • Field numbers 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.
  • 字段号19000至19999保留用于协议缓冲区的实现。如果在消息中使用了其中一个保留字段号,则协议缓冲区编译器将发出抱怨。
  • You cannot use any previously reserved field numbers or any field numbers that have been allocated to extensions.
  • ​不能使用任何以前保留的字段号或已分配给扩展名的任何字段号。

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范围内的字段编号占用两个字节。您可以在协议缓冲区编码中找到更多关于这方面的信息。

Consequences of Reusing Field Numbers
重复使用字段编号的后果

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:

使用一个定义对字段进行编码,然后使用不同的定义对同一字段进行解码,可能会导致:

  • Developer time lost to debugging
  • 开发人员在调试过程中损失的时间
  • A parse/merge error (best case scenario)
  • 解析/合并错误(最佳情况)
  • Leaked PII/SPII
  • 泄露的PII/SPII
  • Data corruption
  • 数据损坏

Common causes of field number reuse:

字段号重复使用的常见原因:

  • renumbering fields (sometimes done to achieve a more aesthetically pleasing number order for fields). Renumbering effectively deletes and re-adds all the fields involved in the renumbering, resulting in incompatible wire-format changes.
  • 对字段重新编号(有时这样做是为了使字段的编号顺序更美观)。重新编号可以有效地删除和重新添加重新编号中涉及的所有字段,从而导致不兼容的导线格式更改。
  • deleting a field and not reserving the number to prevent future 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位,因为有线格式使用三个较低的位。有关此方面的详细信息,请参阅编码主题。

Specifying Field Labels

指定字段标签

Message fields can be one of the following:

消息字段可以是以下字段之一:

  • optional: An optional field is in one of two possible states:

    • optionaloptional字段处于两种可能状态之一:

      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.
    • 该字段未设置,将返回默认值。它不会被序列化到线。

    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编码的更多信息。

Adding More Message Types

添加更多消息类型

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:

  1. message SearchRequest {
  2. string query = 1;
  3. int32 page_number = 2;
  4. int32 results_per_page = 3;
  5. }
  6. message SearchResponse {
  7. ...
  8. }

Adding Comments

添加注释

To add comments to your .proto files, use C/C++-style // and /* ... */ syntax.

要在.proto文件中添加注释,请使用C/C++样式//和/*...*/语法。

  1. /* SearchRequest represents a search query, with pagination options to
  2. * indicate which results to include in the response. */
  3. message SearchRequest {
  4. string query = 1;
  5. int32 page_number = 2; // Which page number do we want?
  6. int32 results_per_page = 3; // Number of results to return per page.
  7. }

Deleting Fields

删除字段

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编码继续解析。

Reserved Fields

保留字段

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.

如果将来的开发人员试图使用这些保留字段号或名称,协议缓冲区编译器会抱怨。

  1. message Foo {
  2. reserved 2, 15, 9 to 11;
  3. reserved "foo", "bar";
  4. }

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语句中混用字段名称和字段编号。

What’s Generated From Your .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上运行协议缓冲区编译器时,编译器会用选择的语言生成代码,需要使用文件中描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,以及从输入流解析消息。

  • 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文件,文件中描述的每个消息类型都有一个类。
  • For Java, the compiler generates a .java file with a class for each message type, as well as a special Builder class for creating message class instances.
  • 对于Java,编译器为每种消息类型生成一个.java文件,其中包含一个类,以及一个用于创建消息类实例的特殊Builder类。
  • 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生成的代码外,编译器还为每个消息类型生成一个.kt文件,其中包含一个DSL,可以用来简化创建消息实例的过程。
  • 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中每个消息类型的静态描述符,然后与元类一起使用,以在运行时创建必要的Python数据访问类。
  • For Go, the compiler generates a .pb.go file with a type for each message type in your file.
  • 对于Go,编译器会生成一个.pb.go文件,其中包含文件中每个消息类型的类型。
  • For Ruby, the compiler generates a .rb file with a Ruby module containing your message types.
  • 对于Ruby,编译器生成一个.rb文件,其中包含一个包含消息类型的Ruby模块。
  • For Objective-C, the compiler generates a pbobjc.h and pbobjc.m file from each .proto, with a class for each message type described in your file.
  • 对于Objective-C,编译器从每个.proto生成一个pbobjc.h和pbobjc.m文件,文件中描述的每个消息类型都有一个类。
  • 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文件,并为文件中描述的每个消息类型提供一个类。
  • For Dart, the compiler generates a .pb.dart file with a class for each message type in your file.
  • 对于Dart,编译器会为文件中的每个消息类型生成一个.pb.dart文件,其中包含一个类。

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版本也即将推出)。

Scalar Value Types

标量值类型

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 TypeNotesC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypeRuby TypeC# TypePHP TypeDart Type
doubledoubledoublefloatfloat64Floatdoublefloatdouble
floatfloatfloatfloatfloat32Floatfloatfloatdouble
int32

Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.

使用可变长度编码。对负数进行编码效率低下–如果的字段可能具有负值,请改用sint32。

int32intintint32Fixnum or Bignum (as required)intintegerint
int64

Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.

使用可变长度编码。编码负数效率低下–如果的字段可能具有负值,请改用sint64。

int64longint/long[4]int64Bignumlonginteger/string[6]Int64
uint32

Uses variable-length encoding.

使用可变长度编码。

uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
uint64

Uses variable-length encoding.

使用可变长度编码。

uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sint32

Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.

使用可变长度编码。带符号的int值。这些比常规int32更有效地编码负数。

int32intintint32Fixnum or Bignum (as required)intintegerint
sint64

Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.

使用可变长度编码。带符号的int值。这些比常规int64更有效地编码负数。

int64longint/long[4]int64Bignumlonginteger/string[6]Int64
fixed32

Always four bytes. More efficient than uint32 if values are often greater than 228.

总是四个字节。如果值经常大于228,则比uint32更有效。

uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
fixed64

Always eight bytes. More efficient than uint64 if values are often greater than 256.

总是八个字节。如果值经常大于256,则比uint64更有效率。

uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sfixed32

Always four bytes.

总是四个字节。

int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64

Always eight bytes.

总是八个字节。

int64longint/long[4]int64Bignumlonginteger/string[6]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
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。

stringStringstr/unicode[5]stringString (UTF-8)stringstringString
bytes

May contain any arbitrary sequence of bytes no longer than 232.

可以包含长度不超过232的任意字节序列。

stringByteStringstr (Python 2)
bytes (Python 3)
[]byteString (ASCII-8BIT)ByteStringstringList

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位计算机。

Default Values

缺省值

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:

解析消息时,如果编码的消息不包含特定的单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:

  • For strings, the default value is the empty string.
  • 对于字符串,默认值为空字符串。
  • For bytes, the default value is empty bytes.
  • 对于字节,默认值为空字节。
  • For bools, the default value is false.
  • 对于布尔,默认值为false。
  • For numeric types, the default value is zero.
  • 对于数字类型,默认值为零。
  • For enums, the default value is the first defined enum value, which must be 0.
  • ​对于枚举,默认值是第一个定义的枚举值,该值必须为0。
  • For message fields, the field is not set. Its exact value is language-dependent. See the generated code guide for details.
  • ​对于消息字段,未设置该字段。它的确切价值取决于语言。有关详细信息,请参阅生成的代码指南。

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.

​有关默认值如何在生成代码中工作的更多详细信息,请参阅所选语言的生成代码指南。

Enumerations

枚举

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 UNIVERSALWEBIMAGESLOCALNEWSPRODUCTS 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类型的字段:

  1. enum Corpus {
  2. CORPUS_UNSPECIFIED = 0;
  3. CORPUS_UNIVERSAL = 1;
  4. CORPUS_WEB = 2;
  5. CORPUS_IMAGES = 3;
  6. CORPUS_LOCAL = 4;
  7. CORPUS_NEWS = 5;
  8. CORPUS_PRODUCTS = 6;
  9. CORPUS_VIDEO = 7;
  10. }
  11. message SearchRequest {
  12. string query = 1;
  13. int32 page_number = 2;
  14. int32 results_per_page = 3;
  15. Corpus corpus = 4;
  16. }

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枚举的第一个常量映射到零:每个枚举定义都必须包含一个映射到零的常量作为其第一个元素。这是因为:

  • There must be a zero value, so that we can use 0 as a numeric default value.
  • ​必须有一个零值,这样我们才能使用0作为数值默认值。
  • The zero value needs to be the first element, for compatibility with the proto2 semantics where the first enum value is always the default.
  • ​零值需要是第一个元素,以与proto2语义兼容,其中第一个枚举值始终是默认值。

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,否则当发现别名时,协议编译器会生成一条警告消息。尽管所有别名值在反序列化期间都是有效的,但在序列化时始终使用第一个值。

  1. enum EnumAllowingAlias {
  2. option allow_alias = true;
  3. EAA_UNSPECIFIED = 0;
  4. EAA_STARTED = 1;
  5. EAA_RUNNING = 1;
  6. EAA_FINISHED = 2;
  7. }
  8. enum EnumNotAllowingAlias {
  9. ENAA_UNSPECIFIED = 0;
  10. ENAA_STARTED = 1;
  11. // ENAA_RUNNING = 1; // Uncommenting this line will cause a warning message.
  12. ENAA_FINISHED = 2;
  13. }

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 enums within a message definition, as in the above example, or outside – these enums 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 enums in your applications, see the generated code guide for your chosen language.

​有关如何在应用程序中使用消息枚举的更多信息,请参阅所选语言的生成代码指南。

Reserved Values

保留值

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关键字指定保留的数值范围最高可达可能的最大值。

  1. enum Foo {
  2. reserved 2, 15, 9 to 11, 40 to max;
  3. reserved "FOO", "BAR";
  4. }

Note that you can’t mix field names and numeric values in the same reserved statement.

请注意,不能在同一个保留语句中混合使用字段名和数值。

Using Other Message Types

使用其他消息类型

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类型的字段:

  1. message SearchResponse {
  2. repeated Result results = 1;
  3. }
  4. message Result {
  5. string url = 1;
  6. string title = 2;
  7. repeated string snippets = 3;
  8. }

Importing Definitions

导入定义

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的任何代码来传递依赖。例如:

  1. // new.proto
  2. // All definitions are moved here
  1. // old.proto
  2. // This is the proto that all clients are importing.
  3. import public "new.proto";
  4. import "other.proto";
  1. // client.proto
  2. import "old.proto";
  3. // 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标志设置为项目的根,并对所有导入使用完全限定的名称。

Using proto2 Message Types

使用proto2消息类型

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消息使用它们也可以)。

Nested Types

嵌套类型

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消息中定义的:

  1. message SearchResponse {
  2. message Result {
  3. string url = 1;
  4. string title = 2;
  5. repeated string snippets = 3;
  6. }
  7. repeated Result results = 1;
  8. }

If you want to reuse this message type outside its parent message type, you refer to it as _Parent_._Type_:

如果要在其父消息类型之外重用此消息类型,请将使用_Parent_._Type_

  1. message SomeOtherMessage {
  2. SearchResponse.Result result = 1;
  3. }

You can nest messages as deeply as you like:

可以任意深度地嵌套消息:

  1. message Outer { // Level 0
  2. message MiddleAA { // Level 1
  3. message Inner { // Level 2
  4. int64 ival = 1;
  5. bool booly = 2;
  6. }
  7. }
  8. message MiddleBB { // Level 1
  9. message Inner { // Level 2
  10. int32 ival = 1;
  11. bool booly = 2;
  12. }
  13. }
  14. }

Updating A Message Type

更新消息类型

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最佳实践和以下规则:

  • Don’t change the field numbers for any existing fields. “Changing” the field number is equivalent to deleting the field and adding a new field with the same type. If you want to renumber a field, see the instructions for deleting a field.
  • ​不要更改任何现有字段的字段编号。“更改”字段编号相当于删除字段并添加具有相同类型的新字段。如果要对字段重新编号,请参见有关删除字段的说明。
  • If you add new fields, any messages serialized by code using your “old” message format can still be parsed by your new generated code. You should keep in mind the default values for these elements so that new code can properly interact with messages generated by old code. Similarly, messages created by your new code can be parsed by your old code: old binaries simply ignore the new field when parsing. See the Unknown Fields section for details.
  • ​如果添加新字段,则任何使用“旧”消息格式由代码序列化的消息仍然可以由新生成的代码解析。应该记住这些元素的默认值,以便新代码能够正确地与旧代码生成的消息交互。类似地,由新代码创建的消息可以由旧代码解析:旧的二进制文件在解析时只需忽略新字段。有关详细信息,请参阅“未知字段”部分。
  • Fields can be removed, as long as the field number is not used again in your updated message type. You may want to rename the field instead, perhaps adding the prefix “OBSOLETE_”, or make the field number reserved, so that future users of your .proto can’t accidentally reuse the number.
  • ​只要在更新的消息类型中不再使用字段号,就可以删除字段。可能需要重命名字段,也许可以添加前缀“OBSOLTE_”,或者保留字段编号,这样.proto的未来用户就不会意外地重复使用该编号。
  • int32uint32int64uint64, 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).
  • int32、uint32、int64、uint64和bool都是兼容的——这意味着您可以将字段从其中一种类型更改为另一种类型,而不会破坏向前或向后的兼容性。如果从线中解析的数字不适合对应的类型,则会得到与在C++中将该数字强制转换为该类型相同的效果(例如,如果将64位数字读取为int32,则会将其截断为32位)。
  • sint32 and sint64 are compatible with each other but are not compatible with the other integer types.
  • sint32和sint64彼此兼容,但与其他整数类型不兼容。
  • string and bytes are compatible as long as the bytes are valid UTF-8.
  • 字符串和字节是兼容的,只要字节是有效的UTF-8。
  • Embedded messages are compatible with bytes if the bytes contain an encoded version of the message.
  • 如果字节包含消息的编码版本,则嵌入消息与字节兼容。
  • fixed32 is compatible with sfixed32, and fixed64 with sfixed64.
  • fixed32与sfixed32兼容,fixed64与sfixer64兼容。
  • For stringbytes, 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.
  • ​对于字符串、字节和消息字段,optional与repeated兼容。给定重复字段的序列化数据作为输入,如果该字段是基元类型字段,则期望该字段为可选字段的客户端将获取最后一个输入值,或者如果该字段为消息类型字段,将合并所有输入元素。请注意,这对于包括布尔和枚举在内的数字类型通常是不安全的。数字类型的重复字段可以以压缩格式进行序列化,当需要可选字段时,压缩格式将无法正确解析。
  • enum is compatible with int32uint32int64, 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.
  • enum在线格式方面与int32、uint32、int64和uint64兼容(请注意,如果值不合适,则会被截断)。但是,请注意,当消息被反序列化时,客户端代码可能会对它们进行不同的处理:例如,无法识别的proto3枚举类型将保留在消息中,但反序列化消息时如何表示取决于语言。Int字段总是保持其值。
  • Changing a single 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

未知字段

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及更高版本中,解析过程中会保留未知字段,并将其包含在序列化输出中。

Any

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。

  1. import "google/protobuf/any.proto";
  2. message ErrorStatus {
  3. string message = 1;
  4. repeated google.protobuf.Any details = 2;
  5. }

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()方法:

  1. // Storing an arbitrary message type in Any.
  2. NetworkErrorDetails details = ...;
  3. ErrorStatus status;
  4. status.add_details()->PackFrom(details);
  5. // Reading an arbitrary message from Any.
  6. ErrorStatus status = ...;
  7. for (const google::protobuf::Any& detail : status.details()) {
  8. if (detail.Is<NetworkErrorDetails>()) {
  9. NetworkErrorDetails network_error;
  10. detail.UnpackTo(&network_error);
  11. ... processing network_error ...
  12. }
  13. }

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消息。

Oneof

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中的顺序确定的最后一个设置值将覆盖之前的所有值。

Using Oneof

使用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:

  1. message SampleMessage {
  2. oneof test_oneof {
  3. string name = 4;
  4. SubMessage sub_message = 9;
  5. }
  6. }

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之一的更多信息。

Oneof Features

Oneof特性

  • 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的所有其他成员。因此,如果设置了几个字段,那么只有设置的最后一个字段仍有值。

    1. SampleMessage message;
    2. message.set_name("name");
    3. CHECK_EQ(message.name(), "name");
    4. // Calling mutable_sub_message() will clear the name field and will set
    5. // sub_message to a new instance of SubMessage with none of its fields set.
    6. message.mutable_sub_message();
    7. 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()方法删除了子消息。

    1. SampleMessage message;
    2. SubMessage* sub_message = message.mutable_sub_message();
    3. message.set_name("name"); // Will delete sub_message
    4. 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

    1. SampleMessage msg1;
    2. msg1.set_name("name");
    3. SampleMessage msg2;
    4. msg2.mutable_sub_message();
    5. msg1.swap(&msg2);
    6. CHECK(msg1.has_sub_message());
    7. CHECK_EQ(msg2.name(), "name");

Backwards-compatibility issues

向后兼容性问题

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的不同版本中的字段。没有办法区分这两者,因为没有办法知道线上的未知字段是否是其中之一的成员。

Tag Reuse Issues
标记重用问题
  • Move fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed. However, you can safely move a single field into a new oneof and may be able to move multiple fields if it is known that only one is ever set. See Updating A Message Type for further details.
  • ​将字段移入或移出oneof:在序列化和解析消息后,可能会丢失一些信息(某些字段将被清除)。但是,可以安全地将单个字段移动到的新字段中,并且如果已知只设置了一个字段,则可以移动多个字段。有关更多详细信息,请参阅更新消息类型。
  • Delete a oneof field and add it back: This may clear your currently set oneof field after the message is serialized and parsed.
  • 删除oneof字段并将其添加回:这可能会在消息序列化和解析后清除当前设置的oneof字段。
  • Split or merge oneof: This has similar issues to moving regular fields.
  • 拆分或合并其中一个:这与移动常规字段有类似的问题。

Maps

关联映射

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;
  • Map fields cannot be repeated.
  • 映射字段不能重复。
  • Wire format ordering and map iteration ordering of map values are undefined, so you cannot rely on your map items being in a particular order.
  • 映射值的线格式排序和映射迭代排序是未定义的,因此不能依赖于映射项的特定顺序。
  • When generating text format for a .proto, maps are sorted by key. Numeric keys are sorted numerically.
  • 为.proto生成文本格式时,映射按键排序。数字键按数字排序。
  • When parsing from the wire or when merging, if there are duplicate map keys the last key seen is used. When parsing a map from text format, parsing may fail if there are duplicate keys.
  • 从线进行解析或合并时,如果存在重复的映射键,则使用最后一个看到的键。从文本格式解析映射时,如果存在重复的键,则解析可能会失败。
  • If you provide a key but no value for a map field, the behavior when the field is serialized is language-dependent. In C++, Java, Kotlin, and Python the default value for the type is serialized, while in other languages nothing is serialized.
  • 如果为映射字段提供了键但没有值,则序列化字段时的行为取决于语言。在C++、Java、Kotlin和Python中,类型的默认值是序列化的,而在其他语言中则不进行任何序列化。

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的更多信息。

Backwards compatibility

向后兼容

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:

映射语法等效于以下内容,因此不支持映射的协议缓冲区实现仍然可以处理数据:

  1. message MapFieldEntry {
  2. key_type key = 1;
  3. value_type value = 2;
  4. }
  5. 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.

任何支持映射的协议缓冲区实现都必须生成并接受上述定义可以接受的数据。

Packages

打包

You can add an optional package specifier to a .proto file to prevent name clashes between protocol message types.

可以将可选的包说明符添加到.proto文件中,以防止协议消息类型之间的名称冲突。

  1. package foo.bar;
  2. message Open { ... }

You can then use the package specifier when defining fields of your message type:

然后,可以在定义消息类型的字段时使用包说明符:

  1. message Foo {
  2. ...
  3. foo.bar.Open open = 1;
  4. ...
  5. }

The way a package specifier affects the generated code depends on your chosen language:

包说明符影响生成代码的方式取决于选择的语言:

  • In C++ the generated classes are wrapped inside a C++ namespace. For example, Open would be in the namespace foo::bar.
  • 在C++中,生成的类被封装在C++命名空间中。例如,Open将位于名称空间foo::bar中。
  • In Java and Kotlin, the package is used as the Java package, unless you explicitly provide an option java_package in your .proto file.
  • 在Java和Kotlin中,除非在.proto文件中明确提供选项Java_package,否则该包将用作Java包。
  • In Python, the package directive is ignored, since Python modules are organized according to their location in the file system.
  • 在Python中,包指令被忽略,因为Python模块是根据它们在文件系统中的位置来组织的。
  • In Go, the package is used as the Go package name, unless you explicitly provide an option go_package in your .proto file.
  • 在Go中,除非在.proto文件中明确提供选项Go_package,否则该包将用作Go包名称。
  • In Ruby, the generated classes are wrapped inside nested Ruby namespaces, converted to the required Ruby capitalization style (first letter capitalized; if the first character is not a letter, PB_ is prepended). For example, Open would be in the namespace Foo::Bar.
  • 在Ruby中,生成的类被封装在嵌套的Ruby名称空间中,转换为所需的Ruby大写样式(第一个字母大写;如果第一个字符不是字母,则PB_是前置的)。例如,Open位于名称空间Foo::Bar中。
  • In C# the package is used as the namespace after converting to PascalCase, unless you explicitly provide an option csharp_namespace in your .proto file. For example, Open would be in the namespace Foo.Bar.
  • 在C#中,除非在.proto文件中明确提供了选项csharp_namespace,否则转换为PascalCase后,包将用作命名空间。例如,Open将位于命名空间Foo.Bar中。

Packages and Name Resolution

程序包和名称解析

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文件来解析所有类型名称。每种语言的代码生成器都知道如何引用该语言中的每种类型,即使它有不同的作用域规则。

Defining Services

定义服务

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文件中定义它,如下所示:

  1. service SearchService {
  2. rpc Search(SearchRequest) returns (SearchResponse);
  3. }

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页面。

JSON Mapping

JSON映射

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编码的输出中包含字段值,即使它是默认值。

proto3JSONJSON exampleNotes
messageobject{"fooBar": v, "g": null, ...}

Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type. However, null cannot be used for the json_name value. For more on why, see Stricter validation for json_name.

​生成JSON对象。消息字段名称被映射到lowerCamelCase并成为JSON对象键。如果指定了json_name字段选项,则指定的值将改为用作键。分析器接受lowerCamelCase名称(或json_name选项指定的名称)和原始proto字段名称。null是所有字段类型的可接受值,并被视为相应字段类型的默认值。但是,不能将null用于json_name值。有关原因的更多信息,请参阅json_name的更严格验证。

enumstring"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 Varray[v, ...]

null is accepted as the empty list [].

null被接受为空列表[]。

booltrue, falsetrue, false
stringstring"Hello World!"
bytesbase64 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, uint32number1, -10, 0

JSON value will be a decimal number. Either numbers or strings are accepted.

JSON值将是一个十进制数字。可以接受数字或字符串。

int64, fixed64, uint64string"1", "-10"

JSON value will be a decimal string. Either numbers or strings are accepted.

JSON值将是一个十进制字符串。可以接受数字或字符串。

float, doublenumber1.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。

Anyobject{"@type": "url", "f": v, ... }

If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy}. Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type.

如果Any包含一个具有特殊JSON映射的值,它将被转换如下:{“@type”:xxx,“value”:yyy}。否则,该值将被转换为JSON对象,并插入“@type”字段以指示实际的数据类型。

Timestampstring"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”以外的偏移也可接受。

Durationstring"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”。

Structobject{ ... }

Any JSON object. See struct.proto.

任何JSON对象。请参阅struct.proto。

Wrapper typesvarious types2, "2", "foo", true, "true", null, 0, ...

Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.

包装器在JSON中使用与包装的基元类型相同的表示,只是在数据转换和传输过程中允许并保留null。

FieldMaskstring"f.fooBar,h"

See field_mask.proto.

请参见field_mask.proto。

ListValuearray[foo, bar, ...]
Valuevalue

Any JSON value. Check google.protobuf.Value for details.

​任何JSON值。有关详细信息,请查看google.protobuf.Value。

NullValuenullJSON null
Emptyobject{}

An empty JSON object

一个空的JSON对象

JSON Options

JSON选项

A proto3 JSON implementation may provide the following options:

proto3 JSON实现可以提供以下选项:

  • Emit fields with default values: Fields with default values are omitted by default in proto3 JSON output. An implementation may provide an option to override this behavior and output fields with their default values.
  • 发出具有默认值的字段:在proto3JSON输出中,默认情况下会省略具有默认值字段。实现可以提供一个选项来覆盖此行为,并使用其默认值输出字段。
  • Ignore unknown fields: Proto3 JSON parser should reject unknown fields by default but may provide an option to ignore unknown fields in parsing.
  • 忽略未知字段:Proto3 JSON解析器默认情况下应拒绝未知字段,但可能提供在解析中忽略未知字段的选项。
  • Use proto field name instead of lowerCamelCase name: By default proto3 JSON printer should convert the field name to lowerCamelCase and use that as the JSON name. An implementation may provide an option to use proto field name as the JSON name instead. Proto3 JSON parsers are required to accept both the converted lowerCamelCase name and the proto field name.
  • 使用proto字段名称而不是lowerCamelCase名称:默认情况下,proto3 JSON打印机应将字段名称转换为lowerCamerCase并将其用作JSON名称。一个实现可以提供一个使用proto字段名称作为JSON名称的选项。Proto3JSON解析器需要接受转换后的lowerCamelCase名称和proto字段名称。
  • Emit enum values as integers instead of strings: The name of an enum value is used by default in JSON output. An option may be provided to use the numeric value of the enum value instead.
  • 将枚举值作为整数而不是字符串发出:JSON输出中默认使用枚举值的名称。可以提供一个选项来代替使用枚举值的数值。

Options

选项

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 SPEEDCODE_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.
    • SPEED(默认):协议缓冲区编译器将生成代码,用于对消息类型进行序列化、解析和执行其他常见操作。此代码经过高度优化。
    • 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.
    • CODE_SIZE:协议缓冲区编译器将生成最小的类,并将依赖于共享的、基于反射的代码来实现串行化、解析和各种其他操作。因此,生成的代码将比SPEED小得多,但操作速度会较慢。类仍将实现与SPEED模式下完全相同的公共API。这种模式在包含大量.proto文件的应用程序中最有用,并且不需要所有文件都非常快。
    • 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.
    • LITE_RUNTIME:协议缓冲区编译器将生成仅依赖于“LITE”运行库(libprotobuf-lite而不是libprotobuf)的类。lite运行时比完整库小得多(大约小一个数量级),但省略了描述符和反射等某些功能。这对于在手机等受限平台上运行的应用程序尤其有用。编译器仍然会像在SPEED模式下一样生成所有方法的快速实现。生成的类将仅在每种语言中实现MessageLite接口,该接口仅提供完整Message接口的方法的子集。
    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

枚举值选项

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:

以下示例显示了添加这些选项的语法:

  1. import "google/protobufs/descriptor.proto";
  2. extend google.protobufs.EnumValueOptions {
  3. optional string string_name = 123456789;
  4. }
  5. enum Data {
  6. DATA_UNKNOWN = 0;
  7. DATA_SEARCH = 1 [deprecated = true];
  8. DATA_DISPLAY = 2 [
  9. (string_name) = "display_value"
  10. ]
  11. }

Continue to the next section, Custom Options to see how to apply custom options to enum values and to fields.

​继续下一节“自定义选项”,了解如何将自定义选项应用于枚举值和字段。

Custom Options

自定义选项

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中的自定义选项。

Option Retention

选项保留

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:

可以直接在选项上设置保留,如下所示:

  1. extend google.protobuf.FileOptions {
  2. optional int32 source_retention_option = 1234
  3. [retention = RETENTION_SOURCE];
  4. }

It can also be set on a plain field, in which case it takes effect only when that field appears inside an option:

它也可以设置在普通字段上,在这种情况下,只有当该字段出现在选项中时,它才会生效:

  1. message OptionsMessage {
  2. int32 source_retention_field = 1 [retention = RETENTION_SOURCE];
  3. }

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支持已经完成,但尚未发布。

Option Targets

选项目标

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:

乍一看,这一功能似乎没有必要,因为每个自定义选项都是特定实体的选项消息的扩展,这已经将选项限制在该实体上。但是,如果将共享选项消息应用于多个实体类型,并且希望控制该消息中各个字段的使用,则选项目标非常有用。例如:

  1. message MyOptions {
  2. string file_only_option = 1 [targets = TARGET_TYPE_FILE];
  3. int32 message_and_enum_option = 2 [targets = TARGET_TYPE_MESSAGE,
  4. targets = TARGET_TYPE_ENUM];
  5. }
  6. extend google.protobuf.FileOptions {
  7. optional MyOptions file_options = 50000;
  8. }
  9. extend google.protobuf.MessageOptions {
  10. optional MyOptions message_options = 50000;
  11. }
  12. extend google.protobuf.EnumOptions {
  13. optional MyOptions enum_options = 50000;
  14. }
  15. // OK: this field is allowed on file options
  16. option (file_options).file_only_option = "abc";
  17. message MyMessage {
  18. // OK: this field is allowed on both message and enum options
  19. option (message_options).message_and_enum_option = 42;
  20. }
  21. enum MyEnum {
  22. MY_ENUM_UNSPECIFIED = 0;
  23. // Error: file_only_option cannot be set on an enum.
  24. option (enum_options).file_only_option = "xyz";
  25. }

Generating Your Classes

生成自己的类

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.
    • ​--cpp_out在DST_DIR中生成C++代码。有关更多信息,请参阅C++生成的代码参考。
    • --java_out generates Java code in DST_DIR. See the Java generated code reference for more.
    • ​--java_out在DST_DIR中生成java代码。有关更多信息,请参阅Java生成的代码参考。
    • --kotlin_out generates additional Kotlin code in DST_DIR. See the Kotlin generated code reference for more.
    • ​--kotlin_out在DST_DIR中生成附加的kotlin代码。有关更多信息,请参阅Kotlin生成的代码参考。
    • --python_out generates Python code in DST_DIR. See the Python generated code reference for more.
    • ​--python_out在DST_DIR中生成python代码。有关更多信息,请参阅Python生成的代码参考。
    • --go_out generates Go code in DST_DIR. See the Go generated code reference for more.
    • ​--go_out在DST_DIR中生成go代码。有关更多信息,请参阅Go生成的代码参考。
    • --ruby_out generates Ruby code in DST_DIR. See the Ruby generated code reference for more.
    • ​--ruby_out在DST_DIR中生成ruby代码。有关更多信息,请参阅Ruby生成的代码参考。
    • --objc_out generates Objective-C code in DST_DIR. See the Objective-C generated code reference for more.
    • ​--objc_out在DST_DIR中生成Objective-C代码。有关更多信息,请参阅Objective-C生成的代码参考。
    • --csharp_out generates C# code in DST_DIR. See the C# generated code reference for more.
    • ​--csharp_out在DST_DIR中生成C#代码。有关更多信息,请参阅C#生成的代码参考。
    • --php_out generates PHP code in DST_DIR. See the PHP generated code reference for more.
    • ​--php_out在DST_DIR中生成php代码。有关更多信息,请参阅PHP生成的代码参考。

    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_PATHs so that the compiler can determine its canonical name.

  • 必须提供一个或多个.proto文件作为输入。可以同时指定多个.proto文件。尽管文件是相对于当前目录命名的,但每个文件都必须位于其中一个IMPORT_PATH中,以便编译器可以确定其规范名称。

File location

文件位置

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。

Location Should be Language-agnostic

位置应与语言无关

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上下文中使用,例如用于测试。

Supported Platforms

支持的平台

For information about:

有关以下方面的信息:

  • the operating systems, compilers, build systems, and C++ versions that are supported, see Foundational C++ Support Policy.
  • ​支持的操作系统、编译器、构建系统和C++版本,请参阅基础C++支持策略。
  • the PHP versions that are supported, see Supported PHP versions.
  • ​支持的PHP版本,请参阅支持的PHP版。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/901790
推荐阅读
相关标签
  

闽ICP备14008679号