前面我们对gRPC框架有了简单了解,按照gRPC的推荐,我们一般使用Protocol Buffers来进行接口缓存,以此来适应不同的语言进行rpc交互,由于proto3具有更多的语言兼容性,所以我们这里主要是学习proto3的语法,然后再针对Go语言如何结合prot3使用做下专门的学习和总结,等有机会我们也可以使用其它语言来实践和总结。
本指南介绍了如何使用协议缓冲区语言来构造协议缓冲区数据,包括.proto文件语法以及如何从.proto文件中生成数据访问类。本总结来自:https://developers.google.com/protocol-buffers/docs/proto 如果有疑问的话可以通过查看原文。
首先让我们看一个非常简单的例子。假设您要定义一个搜索请求消息格式,其中每个搜索请求都有一个查询字符串,您感兴趣的特定结果页面以及每页结果数量。这是.proto用于定义消息类型的文件。
syntax = "proto3";message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;
}
在上面的示例中,所有字段均为标量类型:两个整数(page_number和result_per_page)和一个字符串(query)。但是,您也可以为字段指定复合类型,包括枚举和其他消息类型。
如您所见,消息定义中的每个字段都有一个唯一的编号。这些字段号用于标识消息二进制格式的字段,一旦使用了消息类型,就不应更改这些字段号。请注意,范围为1到15的字段编号需要一个字节来编码,包括字段编号和字段的类型(您可以在Protocol Buffer Encoding中找到有关此内容的更多信息)。在16到2047之间的字段编号占用两个字节。因此,您应该为经常出现的消息元素保留数字1到15。请记住为将来可能添加的频繁出现的元素留出一些空间。
您可以指定最小的场数是1,最大为2^29- 1,或536870911。您也不能使用数字19000到19999(FieldDescriptor::kFirstReservedNumber至FieldDescriptor::kLastReservedNumber),因为它们是为协议缓冲区实现保留的-如果您在中使用这些保留数之一,则协议缓冲区编译器会抱怨.proto。同样,您不能使用任何以前保留的字段号。
消息字段可以是以下内容之一:
在proto3中,repeated标量数字类型的字段packed默认情况下使用编码。
您可以packed在协议缓冲区编码中找到有关编码的更多信息。
可以在一个.proto文件中定义多种消息类型。如果要定义多个相关消息,这很有用–例如,如果要定义与您的SearchResponse消息类型相对应的回复消息格式,可以将其添加到相同的消息中.proto:
message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;
}message SearchResponse {...
}
要将注释添加到.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 result_per_page = 3; // Number of results to return per page.
}
如果您通过完全删除字段或将其注释掉来更新消息类型,将来的用户可以在自己对类型进行更新时重用该字段号。如果他们以后加载旧版本的旧版本,可能会导致严重的问题.proto,包括数据损坏,隐私错误等。确保不会发生这种情况的一种方法是,将已删除字段的字段编号(和/或名称,也可能导致JSON序列化的问题)指定为reserved。如果将来有任何用户尝试使用这些字段标识符,则协议缓冲区编译器将抱怨。
message Foo {reserved 2, 15, 9 to 11;reserved "foo", "bar";
}
请注意,您不能在同reserved一条语句中混用字段名称和字段编号。
当你在运行protocol buffer compiler处理一个.proto文件时,编译器会以您选择的语言生成代码,您将需要使用文件中描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,并从输入流中解析消息。
通过遵循所选语言的教程(即将推出proto3版本),您可以找到有关每种语言使用API的更多信息。有关API的更多详细信息,请参见相关的API参考(proto3版本也即将推出)。
标量消息字段可以具有以下类型之一-该表显示.proto文件中指定的类型,以及自动生成的类中的相应类型:
.proto Type | Notes | C++ Type | Java Type | Python Type[2] | 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 | 使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint32。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
int64 | 使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint64。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
uint32 | 使用可变长度编码。 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
uint64 | 使用可变长度编码。 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong | integer/string[5] | Int64 |
sint32 | 使用可变长度编码。有符号的int值。与常规int32相比,它们更有效地对负数进行编码。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sint64 | 使用可变长度编码。有符号的int值。与常规int64相比,它们更有效地编码负数。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
fixed32 | 始终为四个字节。如果值通常大于2 28,则比uint32更有效。 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
fixed64 | 始终为八个字节。如果值通常大于2 56,则比uint64更有效。 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong | integer/string[5] | Int64 |
sfixed32 | 始终为四个字节。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sfixed64 | 始终为八个字节。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | 字符串必须始终包含UTF-8编码或7位ASCII文本,并且不能超过2^32。 | string | String | str/unicode[4] | string | String (UTF-8) | string | string | String |
bytes | 可以包含任意长度不超过2^32的字节序列。 | string | ByteString | str | []byte | String (ASCII-8BIT) | ByteString | string |
当您在Protocol Buffer Encoding中对消息进行序列化时,您可以找到更多有关如何编码这些类型的信息。
[1]在Java中,无符号的32位和64位整数使用带符号的对等体表示,最高位仅存储在符号位中。
[2]在所有情况下,将值设置为字段都会执行类型检查以确保其有效。
[3] 64位或无符号32位整数在解码时总是表示为long,但是如果在设置字段时给出了int,则可以为int。在所有情况下,该值都必须适合设置时表示的类型。参见[2]。
[4] Python字符串在解码时表示为unicode,但如果给出了ASCII字符串,则可以为str(此字符串可能会发生变化)。
[5]在64位计算机上使用Integer,在32位计算机上使用string。
解析消息时,如果编码的消息不包含特定的单数元素,则已解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:
重复字段的默认值是空的(通常是使用适当语言的空列表)。
请注意,对于标量消息字段,一旦解析了一条消息,就无法告诉该字段是被显式设置为默认值(例如,是否将boolean设置为false)还是根本没有设置:您应该牢记这一点在定义您的消息类型时。例如,false如果您不希望默认情况下也发生这种行为,则在设置为时,没有布尔值会打开某些行为。还要注意的是,如果一个标消息字段被设置为默认值,该值将不会在电线上连载。
有关默认值在生成的代码中如何工作的更多详细信息,请参见所选语言的生成的代码指南。
定义消息类型时,您可能希望其字段之一仅具有一个预定义的值列表之一。例如,假设你想添加一个corpus字段每个SearchRequest,其中语料库可以UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO。您可以通过enum在消息定义中为每个可能的值添加一个常量来非常简单地执行此操作。
在下面的示例中,我们添加了一个带有所有可能值的enum被叫项Corpus,以及一个type字段Corpus:
message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;enum Corpus {UNIVERSAL = 0;WEB = 1;IMAGES = 2;LOCAL = 3;NEWS = 4;PRODUCTS = 5;VIDEO = 6;}Corpus corpus = 4;
}
如您所见,Corpus枚举的第一个常量映射为零:每个枚举定义必须包含一个映射为零的常量作为其第一个元素。这是因为:
您可以通过将相同的值分配给不同的枚举常量来定义别名。为此,您需要将allow_alias选项设置为true,否则协议别名会在找到别名时生成一条错误消息。
message MyMessage1 {enum EnumAllowingAlias {option allow_alias = true;UNKNOWN = 0;STARTED = 1;RUNNING = 1;}
}
message MyMessage2 {enum EnumNotAllowingAlias {UNKNOWN = 0;STARTED = 1;// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.}
}
枚举器常量必须在32位整数范围内。由于enum值在导线上使用varint编码,因此负值效率不高,因此不建议使用。您可以enum在消息定义中定义,如上面的示例所示,enum也可以在外部定义-这些s可以在.proto文件中的任何消息定义中重复使用。您还可以使用enum语法将一条消息中声明的类型用作另一条消息中字段的类型_MessageType_.EnumType。
当运行在所述协议缓冲编译器.proto,它使用一个enum,生成的代码将具有对应enum于Java或C ++,一个特殊EnumDescriptor的是被用于创建一组与在所述运行时生成的类的整数值的符号常数的Python类。
注意: 生成的代码可能会受到特定于语言的枚举数限制(一种语言的成千上万个)。请查看您计划使用的语言的限制。
反序列化期间,无法识别的枚举值将保留在消息中,尽管在反序列化消息时如何表示该值取决于语言。在支持具有超出指定符号范围的值的开放式枚举类型的语言(例如C ++和Go)中,未知的枚举值只是作为其基础整数表示形式存储。在具有封闭枚举类型的语言(例如Java)中,枚举中的大小写用于表示无法识别的值,并且可以使用特殊的访问器访问基础整数。在任何一种情况下,如果消息已序列化,则无法识别的值仍将与消息一起序列化。
有关如何enum在应用程序中使用message的更多信息,请参见针对所选语言生成的代码指南。
如果您通过完全删除枚举条目或将其注释掉来更新枚举类型,则将来的用户在自己对类型进行更新时可以重复使用数值。如果他们以后加载旧版本的旧版本,可能会导致严重的问题.proto,包括数据损坏,隐私错误等。确保不会发生这种情况的一种方法是,将已删除的条目的数字值(和/或名称,也可能导致JSON序列化的问题)指定为reserved。如果将来有任何用户尝试使用这些标识符,则协议缓冲区编译器会抱怨。您可以使用max关键字指定保留的数值范围达到最大可能值。
enum Foo {reserved 2, 15, 9 to 11, 40 to max;reserved "FOO", "BAR";
}
请注意,您不能在同reserved一条语句中混合使用字段名和数字值。
您可以使用其他消息类型作为字段类型。例如,假设你想包括Result每个消息的SearchResponse消息-要做到这一点,你可以定义一个Result在同一个消息类型.proto,然后指定类型的字段Result中SearchResponse:
message SearchResponse {repeated Result results = 1;
}message Result {string url = 1;string title = 2;repeated string snippets = 3;
}
请注意,此功能在Java中不可用。
在上面的示例中,Result消息类型与以下文件定义在同一文件中SearchResponse–如果要用作字段类型的消息类型已在另一个.proto文件中定义,该怎么办?
您可以.proto通过导入其他文件来使用它们的定义。要导入another.proto的定义,请在文件顶部添加一个import语句:
import "myproject/other_protos.proto";
默认情况下,您只能使用直接导入.proto文件中的定义。但是,有时您可能需要将.proto文件移动到新位置。.proto现在,您可以直接.proto在原位置放置一个虚拟文件,以使用该import public概念将所有导入转发到新位置,而不必一次移动文件并一次更改所有呼叫站点。import public任何导入包含该import public语句的原型的人都可以可传递地依赖依赖项。例如:
// 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
协议编译器使用-I/–proto_path标志在协议编译器命令行上指定的一组目录中搜索导入的文件。如果未给出标志,它将在调用编译器的目录中查找。通常,应将–proto_path标志设置为项目的根,并对所有导入使用完全限定的名称。
可以导入proto2消息类型并在proto3消息中使用它们,反之亦然。但是,不能直接在proto3语法中使用proto2枚举(如果导入的proto2消息使用它们,也可以)。
您可以在其他消息类型中定义和使用消息类型,如以下示例所示–在此处,Result消息是在SearchResponse消息内部定义的:
message SearchResponse {message Result {string url = 1;string title = 2;repeated string snippets = 3;}repeated Result results = 1;
}
如果要在其父消息类型之外重用此消息类型,则将其称为_Parent_.Type:
message SomeOtherMessage {SearchResponse.Result result = 1;
}
您可以根据需要深度嵌套消息:
message Outer { // Level 0message MiddleAA { // Level 1message Inner { // Level 2int64 ival = 1;bool booly = 2;}}message MiddleBB { // Level 1message Inner { // Level 2int32 ival = 1;bool booly = 2;}}
}
如果现有消息类型不再满足您的所有需求(例如,您希望消息格式具有一个额外的字段),但是您仍然希望使用以旧格式创建的代码,请不要担心!在不破坏任何现有代码的情况下更新消息类型非常简单。只要记住以下规则:
未知字段是格式正确的协议缓冲区序列化数据,表示解析器无法识别的字段。例如,当旧二进制文件使用新字段解析新二进制文件发送的数据时,这些新字段将成为旧二进制文件中的未知字段。
最初,proto3消息始终在解析过程中丢弃未知字段,但是在版本3.5中,我们重新引入了保留未知字段以匹配proto2行为的功能。在版本3.5和更高版本中,未知字段将在解析期间保留并包含在序列化输出中。
该Any消息类型,可以使用邮件作为嵌入式类型,而不必自己.proto定义。一个Any含有任意的序列化消息bytes,以充当一个全局唯一标识符和解析为消息的类型的URL一起。要使用该Any类型,您需要导入 google/protobuf/any.proto。
import "google/protobuf/any.proto";message ErrorStatus {string message = 1;repeated google.protobuf.Any details = 2;
}
给定消息类型的默认类型URL是 type.googleapis.com/_packagename_._messagename_
。
不同的语言实现将支持运行时库帮助程序以类型安全的方式打包和解压缩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 Any& detail : status.details()) {if (detail.Is
}
当前,正在开发用于任何类型的运行时库。
如果您已经熟悉proto2语法,则Any可以容纳任意proto3消息,类似于可以允许扩展的proto2消息。
如果您的消息包含多个字段,并且最多同时设置一个字段,则可以使用oneof功能强制执行此行为并节省内存。
一个字段与常规字段类似,不同之处在于一个共享内存中的所有字段,并且最多可以同时设置一个字段。设置oneof中的任何成员会自动清除所有其他成员。您可以根据所选择的语言,使用特殊case()或WhichOneof()方法来检查其中一个设置的值(如果有)。
要在您的文件中定义oneof,请.proto使用oneof关键字,后跟您的oneof名称,在这种情况下test_oneof:
message SampleMessage {oneof test_oneof {string name = 4;SubMessage sub_message = 9;}
}
然后,将您的oneof字段添加到oneof定义。您可以添加任何类型的map字段,但字段和repeated字段除外。
在生成的代码中,oneof字段具有与常规字段相同的getter和setter。您还将获得一种特殊的方法来检查oneof中的哪个值(如果有)。您可以在相关API参考中找到有关所选语言的oneof API的更多信息。
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name field.
CHECK(!message.has_name());
如果解析器在线路上遇到同一个对象的多个成员,则在解析的消息中仅使用最后看到的成员。
一个不能是repeated。
反射API适用于其中一个字段。
如果将oneof字段设置为默认值(例如将int32 oneof字段设置为0),则将设置该oneof字段的“大小写”,并且该值将在线路上序列化。
如果您使用的是C ++,请确保您的代码不会导致内存崩溃。以下示例代码将崩溃,因为sub_message已通过调用该set_name()方法将其删除。
SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name"); // Will delete sub_message
sub_message->set_... // Crashes here
SampleMessage msg1;
msg1.set_name("name");
SampleMessage msg2;
msg2.mutable_sub_message();
msg1.swap(&msg2);
CHECK(msg1.has_sub_message());
CHECK(msg2.has_name());
添加或删除字段之一时请多加注意。如果检查oneof的值返回None/ NOT_SET,则可能意味着尚未设置oneof或已将其设置为oneof的不同版本中的字段。由于无法知道导线上的未知字段是否是oneof的成员,因此无法分辨出两者之间的区别。
标签重用问题
如果要创建关联映射作为数据定义的一部分,则协议缓冲区提供了一种方便的快捷方式语法:
map
…其中key_type可以是任何整数或字符串类型(因此,除浮点类型和以外的任何标量类型bytes)。请注意,枚举不是有效的key_type。的value_type可以是任何类型的除另一map。
因此,例如,如果您想创建一个project map,其中每个Project消息都与一个字符串键相关联,则可以这样定义它:
map
生成的map API当前可用于所有proto3支持的语言。您可以在相关API参考中找到有关所选语言的map API的更多信息。
映射语法与网上的以下语法等效,因此不支持映射的协议缓冲区实现仍可以处理您的数据:
message MapFieldEntry {key_type key = 1;value_type value = 2;
}repeated MapFieldEntry map_field = N;
任何支持映射的协议缓冲区实现都必须生成并接受可以被上述定义接受的数据。
您可以package在.proto文件中添加可选的说明符,以防止协议消息类型之间的名称冲突。
package foo.bar;
message Open { ... }
然后,您可以在定义消息类型的字段时使用包说明符:
message Foo {...foo.bar.Open open = 1;...
}
包说明符影响生成的代码的方式取决于您选择的语言:
协议缓冲语言中的类型名称解析类似于C ++:首先搜索最内层的作用域,然后搜索下一个最内层的作用,依此类推,每个包都被视为其父包“内在”。领先的“。” (例如,.foo.bar.Baz)表示从最外面的范围开始。
协议缓冲区编译器通过解析导入的.proto文件来解析所有类型名称。每种语言的代码生成器都知道如何引用该语言中的每种类型,即使它具有不同的范围规则。
如果要将消息类型与RPC(远程过程调用)系统一起使用,则可以在.proto文件中定义RPC服务接口,并且协议缓冲区编译器将以您选择的语言生成服务接口代码和存根。因此,例如,如果您想使用一种方法来定义RPC服务,该方法接受您的方法SearchRequest并返回SearchResponse,则可以在.proto文件中按以下方式对其进行定义:
service SearchService {rpc Search(SearchRequest) returns (SearchResponse);
}
与协议缓冲区一起使用的最直接的RPC系统是gRPC:这是Google开发的与语言和平台无关的开源RPC系统。gRPC与协议缓冲区配合使用特别好,它使您可以.proto使用特殊的协议缓冲区编译器插件直接从文件中生成相关的RPC代码。
如果您不想使用gRPC,也可以在自己的RPC实现中使用协议缓冲区。您可以在《Proto2语言指南》中找到有关此内容的更多信息。
还有许多正在进行的第三方项目正在为协议缓冲区开发RPC实现。有关我们知道的项目的链接列表,请参阅第三方加载项Wiki页面。
Proto3支持JSON中的规范编码,从而使在系统之间共享数据更加容易。下表中按类型对编码进行了描述。
如果JSON编码的数据中缺少某个值,或者该值为null,则在解析为协议缓冲区时,它将被解释为适当的默认值。如果字段在协议缓冲区中具有默认值,则默认情况下会在JSON编码的数据中将其省略以节省空间。实现可以提供选项,以在JSON编码的输出中发出具有默认值的字段。
proto3 | JSON | JSON example | Notes |
---|---|---|---|
message | object | {"fooBar": v, "g": null, …} | 生成JSON对象。消息字段名被映射到lowerCamelCase并成为JSON对象键。如果指定了’ json_name ‘字段选项,则将使用指定的值作为键值。解析器接受lowerCamelCase名称(或由’ json_name ‘选项指定的名称)和原始字段名称。’ null '是所有字段类型的可接受值,并作为相应字段类型的默认值处理。 |
enum | string | "FOO_BAR" | 使用proto中指定的枚举值的名称。解析器同时接受枚举名称和整数值。 |
map | object | {"k": v, …} | 所有键都被转换为字符串。 |
repeated V | array | [v, …] | ’ null ‘被接受为空列表’[]’。 |
bool | true, false | true, false | |
string | string | "Hello World!" | |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" | JSON值将是使用带填充的标准base64编码的字符串编码的数据。可以接受标准或url安全的base64编码(带/不带填充)。 |
int32, fixed32, uint32 | number | 1, -10, 0 | JSON值将是一个十进制数。可以接受数字或字符串。 |
int64, fixed64, uint64 | string | "1", "-10" | JSON值将是一个十进制字符串。可以接受数字或字符串。 |
float, double | number | 1.1, -10.0, 0, "NaN", "Infinity" | JSON值将是一个数字或特殊字符串值“NaN”、“Infinity”和“-Infinity”中的一个。可以接受数字或字符串。指数表示法也被接受。-0被认为等同于0。 |
Any | object | {"@type": "url", "f": v, … } | 如果Any中包含一个具有特殊JSON映射的值,则转换为`{"@type": xxx, “value”: yyy} ‘。否则,该值将被转换为JSON对象,并插入’ @type '字段来指示实际的数据类型。 |
Timestamp | string | "1972-01-01T10:00:20.021Z" | 使用RFC 3339,其中生成的输出将始终是z标准化的,并使用0、3、6或9小数位数。除“Z”外的偏移也是可以接受的。 |
Duration | string | "1.000340012s", "1s" | 生成的输出总是包含0、3、6或9个小数,这取决于所需的精度,然后加上后缀“s”。只要符合纳秒精度,并且需要后缀“s”,就可以接受任何小数(也可以是零)。 |
Struct | object | { … } | 任何一个JSON对象。看到“struct.proto”。 |
Wrapper types | various types | 2, "2", "foo", true, "true", null, 0, … | 包装器在JSON中使用与包装原始类型相同的表示,除了在数据转换和传输期间允许并保留“null”。 |
FieldMask | string | "f.fooBar,h" | 看到“字段mask.proto”。 |
ListValue | array | [foo, bar, …] | |
Value | value | 任何JSON值。查看google.protobuf.Value获取详细信息。 | |
NullValue | null | JSON null | |
Empty | object | {} | 空JSON对象 |
一个proto3 JSON实现可以提供以下选项:
.proto
文件中的各个声明可以使用许多选项来注释。选项不会改变声明的整体含义,但可能会影响在特定上下文中处理声明的方式。可用选项的完整列表在中定义google/protobuf/descriptor.proto
。
一些选项是文件级选项,这意味着它们应在顶级范围内编写,而不是在任何消息,枚举或服务定义内。一些选项是消息级别的选项,这意味着它们应该写在消息定义中。一些选项是字段级选项,这意味着它们应在字段定义中编写。选项也可以写在枚举类型,枚举值,字段,服务类型和服务方法中;但是,目前没有针对这些选项的有用选项。
以下是一些最常用的选项:
java_package
(文件选项):您要用于生成的Java类的包。如果文件中未提供任何显式java_package
选项.proto
,则默认情况下将使用原型包(在.proto
文件中使用“ package”关键字指定)。但是,proto软件包通常不能成为良好的Java软件包,因为proto软件包不应以反向域名开头。如果未生成Java代码,则此选项无效。option java_package = "com.example.foo";
java_outer_classname
(文件选项):您要生成的包装Java类的类名(以及文件名)。如果java_outer_classname
在.proto
文件中未指定任何显式名称,则通过将.proto
文件名转换为驼峰大小写来构造类名(因此foo_bar.proto
成为FooBar.java
)。如果java_multiple_files
禁用该选项,则所有其他类/枚举/等。为.proto
文件生成的文件将在此外部包装Java类中作为嵌套类/枚举/等生成。如果未生成Java代码,则此选项无效。option java_outer_classname = "Ponycopter";
java_multiple_files
(文件选项):如果为false,.java
则将为此.proto
文件仅生成一个文件,以及所有Java类/枚举/等。为顶级消息,服务和枚举生成的消息将嵌套在外部类的内部(请参阅参考资料java_outer_classname
)。如果为true,.java
则将为每个Java类/枚举/等生成单独的文件。是为顶级消息,服务和枚举生成的,而为此.proto
文件生成的包装Java类将不包含任何嵌套的类/枚举/等。这是一个布尔选项,默认为false
。如果未生成Java代码,则此选项无效。option java_multiple_files = true;
optimize_for
(文件选项):可以设置为SPEED
,CODE_SIZE
或LITE_RUNTIME
。这会通过以下方式影响C ++和Java代码生成器(可能还有第三方生成器): SPEED
(默认值):协议缓冲区编译器将生成用于对消息类型进行序列化,解析和执行其他常见操作的代码。此代码已高度优化。CODE_SIZE
:协议缓冲区编译器将生成最少的类,并将依赖于基于反射的共享代码来实现序列化,解析和其他各种操作。因此,生成的代码将比使用的代码小得多SPEED
,但操作会更慢。类仍将实现与SPEED
模式下完全相同的公共API 。此模式在包含大量.proto
文件且不需要所有文件快速快速运行的应用程序中最有用。LITE_RUNTIME
:协议缓冲区编译器将生成仅依赖于“精简版”运行时库(libprotobuf-lite
而不是libprotobuf
)的类。精简版运行时比完整库要小得多(大约小一个数量级),但省略了某些功能,例如描述符和反射。这对于在受限平台(如手机)上运行的应用程序特别有用。如同在SPEED
模式下一样,编译器仍将生成所有方法的快速实现。生成的类将仅以MessageLite
每种语言实现该接口,该语言仅提供完整Message
接口的方法的子集。option optimize_for = CODE_SIZE;
cc_enable_arenas
(文件选项):启用C ++生成代码的arena allocation。objc_class_prefix
(文件选项):设置Objective-C类的前缀,该前缀是所有Object-C生成的类和此.proto枚举的前缀。没有默认值。您应该使用Apple推荐的3-5个大写字符之间的前缀。请注意,所有2个字母前缀均由Apple保留。deprecated
(字段选项):如果设置为true
,则表明该字段已弃用,并且不应被新代码使用。在大多数语言中,这没有实际效果。在Java中,这成为@Deprecated
注释。将来,其他特定于语言的代码生成器可能会在字段的访问器上生成弃用注释,这反过来将导致在编译尝试使用该字段的代码时发出警告。如果该字段未被任何人使用,并且您想阻止新用户使用该字段,请考虑使用保留语句替换该字段声明。int32 old_field = 6 [deprecated = true];
协议缓冲区还允许您定义和使用自己的选项。这是大多数人不需要的高级功能。如果您确实需要创建自己的选项,请参阅《Proto2语言指南》以获取详细信息。请注意,创建自定义选项使用扩展名,只有proto3中的自定义选项才允许使用扩展名。
要生成在规定的消息类型的使用Java,Python,C ++,Go,Ruby,Objective-C的,或C#的代码的.proto
文件,你需要运行协议缓冲编译器protoc
上.proto
。如果尚未安装编译器,请下载软件包并按照自述文件中的说明进行操作。对于Go,您还需要为编译器安装一个特殊的代码生成器插件:您可以在GitHub上的golang / protobuf存储库中找到此代码和安装说明。
协议编译器的调用如下:
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
指定.proto
解析import
指令时要在其中查找文件的目录。如果省略,则使用当前目录。可以通过--proto_path
多次传递选项来指定多个导入目录。将按顺序搜索它们。-I=_IMPORT_PATH_
可以用作的简写形式--proto_path
。--cpp_out
在中生成C ++代码DST_DIR
。有关更多信息,请参见C ++生成的代码参考。--java_out
在中生成Java代码DST_DIR
。有关更多信息,请参见Java生成的代码参考。--python_out
在中生成Python代码DST_DIR
。有关更多信息,请参见Python生成的代码参考。--go_out
在中生成Go代码DST_DIR
。有关更多信息,请参见Go生成的代码参考。--ruby_out
在中生成Ruby代码DST_DIR
。Ruby生成的代码参考即将推出!--objc_out
在中生成Objective-C代码DST_DIR
。有关更多信息,请参见Objective-C生成的代码参考。--csharp_out
在中生成C#代码DST_DIR
。有关更多信息,请参见C#生成的代码参考。--php_out
在中生成PHP代码DST_DIR
。参见PHP生成的代码参考以获取更多信息。为方便起见,如果DST_DIR
结尾为.zip
或.jar
,编译器会将输出写入具有给定名称的单个ZIP格式的存档文件中。.jar
根据Java JAR规范的要求,还将为输出提供清单文件。请注意,如果输出归档文件已经存在,它将被覆盖;否则,输出归档文件将被覆盖。编译器不够智能,无法将文件添加到现有存档中。.proto
文件作为输入。.proto
可以一次指定多个文件。尽管这些文件是相对于当前目录命名的,但是每个文件都必须位于IMPORT_PATH
s之一中,以便编译器可以确定其规范名称。