Protobuf2
定义一个音讯类型
先来看一个非常简单的例子。假如你想定义一个“搜寻申请”的音讯格局,每一个申请含有一个查问字符串、你感兴趣的查问后果所在的页数,以及每一页多少条查问后果。能够采纳如下的形式来定义音讯类型的.proto文件了:
syntax = "proto3";message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3;}
文件的第一行指定了你正在应用proto3语法:如果你没有指定这个,编译器会应用proto2。这个指定语法行必须是文件的非空非正文的第一个行。
SearchRequest音讯格局有3个字段,在音讯中承载的数据别离对应于每一个字段。其中每个字段都有一个名字和一种类型。
指定字段类型
在下面的例子中,所有字段都是标量类型:两个整型(page_number和result_per_page),一个string类型(query)。当然,你也能够为字段指定其余的合成类型,包含枚举(enumerations)或其余音讯类型。
调配标识号
正如你所见,在音讯定义中,每个字段都有惟一的一个数字标识符。这些标识符是用来在音讯的二进制格局中辨认各个字段的,一旦开始应用就不可能再扭转。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁呈现的音讯元素保留 [1,15]之内的标识号。切记:要为未来有可能增加的、频繁呈现的标识号预留一些标识号。
最小的标识号能够从1开始,最大到2^29 - 1, or 536,870,911。不能够应用其中的[19000-19999]( (从FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的标识号, Protobuf协定实现中对这些进行了预留。如果非要在.proto文件中应用这些预留标识号,编译时就会报警。同样你也不能应用晚期保留的标识号。
指定字段规定
所指定的音讯字段修饰符必须是如下之一:
- singular:一个格局良好的音讯应该有0个或者1个这种字段(然而不能超过1个)。
- repeated:在一个格局良好的音讯中,这种字段能够反复任意屡次(包含0次)。反复的值的程序会被保留。
在proto3中,repeated的标量域默认状况虾应用packed。
你能够理解更多的pakced属性在Protocol Buffer 编码
增加更多音讯类型
在一个.proto文件中能够定义多个音讯类型。在定义多个相干的音讯的时候,这一点特地有用——例如,如果想定义与SearchResponse音讯类型对应的回复音讯格局的话,你能够将它增加到雷同的.proto文件中,如:
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3;}message SearchResponse { ...}
增加正文
向.proto文件增加正文,能够应用C/C++/Java格调的双斜杠(//) 语法格局,如:
message SearchRequest { string query = 1; int32 page_number = 2; // Which page number do we want? int32 result_per_page = 3; // Number of results to return per page.}
保留标识符(Reserved)
如果你通过删除或者正文所有域,当前的用户在更新这个类型的时候可能重用这些标识号。如果你应用旧版本加载雷同的.proto文件会导致重大的问题,包含数据损坏、隐衷谬误等等。当初有一种确保不会产生这种状况的办法就是为字段tag(reserved name可能会JSON序列化的问题)指定reserved标识符,protocol buffer的编译器会正告将来尝试应用这些域标识符的用户。
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar";}
注:不要在同一行reserved申明中同时申明域名字和tag number。
从.proto文件生成了什么?
当用protocol buffer编译器来运行.proto文件时,编译器将生成所抉择语言的代码,这些代码能够操作在.proto文件中定义的音讯类型,包含获取、设置字段值,将音讯序列化到一个输入流中,以及从一个输出流中解析音讯。
- 对C++来说,编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个音讯有一个对应的类。
- 对Java来说,编译器为每一个音讯类型生成了一个.java文件,以及一个非凡的Builder类(该类是用来创立音讯类接口的)。
- 对Python来说,有点不太一样——Python编译器为.proto文件中的每个音讯类型生成一个含有动态描述符的模块,,该模块与一个元类(metaclass)在运行时(runtime)被用来创立所需的Python数据拜访类。
- 对go来说,编译器会位每个音讯类型生成了一个.pd.go文件。
- 对于Ruby来说,编译器会为每个音讯类型生成了一个.rb文件。
- javaNano来说,编译器输入相似域java然而没有Builder类
- 对于Objective-C来说,编译器会为每个音讯类型生成了一个pbobjc.h文件和pbobjcm文件,.proto文件中的每一个音讯有一个对应的类。
- 对于C#来说,编译器会为每个音讯类型生成了一个.cs文件,.proto文件中的每一个音讯有一个对应的类。
你能够从如下的文档链接中获取每种语言更多API(proto3版本的内容很快就颁布)。API Reference
标量数值类型
一个标量音讯字段能够含有一个如下的类型——该表格展现了定义于.proto文件中的类型,以及与之对应的、在主动生成的拜访类中定义的类型:
你能够在文章Protocol Buffer 编码中,找到更多“序列化音讯时各种类型如何编码”的信息。
- 在java中,无符号32位和64位整型被示意成他们的整型对应模式,最高位被贮存在标记位中。
- 对于所有的状况,设定值会执行类型查看以确保此值是无效。
- 64位或者无符号32位整型在解码时被示意成为ilong,然而在设置时能够应用int型值设定,在所有的状况下,值必须合乎其设置其类型的要求。
- python中string被示意成在解码时示意成unicode。然而一个ASCIIstring能够被示意成str类型。
- Integer在64位的机器上应用,string在32位机器上应用
默认值
当一个音讯被解析的时候,如果被编码的信息不蕴含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:
- 对于string,默认是一个空string
- 对于bytes,默认是一个空的bytes
- 对于bool,默认是false
- 对于数值类型,默认是0
- 对于枚举,默认是第一个定义的枚举值,必须为0;
- 对于音讯类型(message),域没有被设置,确切的音讯是依据语言确定的,详见generated code guide
- 对于可反复域的默认值是空(通常状况下是对应语言中空列表)。
注:对于标量音讯域,一旦音讯被解析,就无奈判断域开释被设置为默认值(例如,例如boolean值是否被设置为false)还是基本没有被设置。你应该在定义你的音讯类型时十分留神。例如,比方你不应该定义boolean的默认值false作为任何行为的触发形式。也应该留神如果一个标量音讯域被设置为标记位,这个值不应该被序列化传输。
查看generated code guide抉择你的语言的默认值的工作细节。
枚举
当须要定义一个音讯类型的时候,可能想为一个字段指定某“预约义值序列”中的一个值。例如,假如要为每一个SearchRequest音讯增加一个 corpus字段,而corpus的值可能是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一个。 其实能够很容易地实现这一点:通过向音讯定义中增加一个枚举(enum)并且为每个可能的值定义一个常量就能够了。
在上面的例子中,在音讯格局中增加了一个叫做Corpus的枚举类型——它含有所有可能的值 ——以及一个类型为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枚举的第一个常量映射为0:每个枚举类型必须将其第一个类型映射为0,这是因为:
- 必须有有一个0值,咱们能够用这个0值作为默认值。
这个零值必须为第一个元素,为了兼容proto2语义,枚举类的第一个值总是默认值。
你能够通过将不同的枚举常量指定位雷同的值。如果这样做你须要将allow_alias设定位true,否则编译器会在别名的中央产生一个错误信息。enum EnumAllowingAlias {option allow_alias = true;UNKNOWN = 0;STARTED = 1;RUNNING = 1;}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值是应用可变编码方式的,对正数不够高效,因而不举荐在enum中应用正数。如上例所示,能够在 一个音讯定义的外部或内部定义枚举——这些枚举能够在.proto文件中的任何音讯定义里重用。当然也能够在一个音讯中申明一个枚举类型,而在另一个不同 的音讯中应用它——采纳MessageType.EnumType的语法格局。
当对一个应用了枚举的.proto文件运行protocol buffer编译器的时候,生成的代码中将有一个对应的enum(对Java或C++来说),或者一个非凡的EnumDescriptor类(对 Python来说),它被用来在运行时生成的类中创立一系列的整型值符号常量(symbolic constants)。
在反序列化的过程中,无奈辨认的枚举值会被保留在音讯中,尽管这种示意形式须要根据所应用语言而定。在那些反对凋谢枚举类型超出指定范畴之外的语言中(例如C++和Go),为辨认的值会被示意成所反对的整型。在应用关闭枚举类型的语言中(Java),应用枚举中的一个类型来示意未辨认的值,并且能够应用所反对整型来拜访。在其余状况下,如果解析的音讯被序列号,未辨认的值将放弃原样。
对于如何在你的应用程序的音讯中应用枚举的更多信息,请查看所抉择的语言generated code guide。
应用其余音讯类型
你能够将其余音讯类型用作字段类型。例如,假如在每一个SearchResponse音讯中蕴含Result音讯,此时能够在雷同的.proto文件中定义一个Result音讯类型,而后在SearchResponse音讯中指定一个Result类型的字段,如:
message SearchResponse { repeated Result results = 1;}message Result { string url = 1; string title = 2; repeated string snippets = 3;}
导入定义
在下面的例子中,Result音讯类型与SearchResponse是定义在同一文件中的。如果想要应用的音讯类型曾经在其余.proto文件中曾经定义过了呢?
你能够通过导入(importing)其余.proto文件中的定义来应用它们。要导入其余.proto文件的定义,你须要在你的文件中增加一个导入申明,如:
import "myproject/other_protos.proto";
默认状况下你只能应用间接导入的.proto文件中的定义. 然而, 有时候你须要挪动一个.proto文件到一个新的地位, 能够不间接挪动.proto文件, 只需放入一个伪 .proto 文件在老的地位, 而后应用import public转向新的地位。import public 依赖性会通过任意导入蕴含import public申明的proto文件传递。例如:
// 这是新的proto// All definitions are moved here
// 这是久的proto// 这是所有客户端正在导入的包import public "new.proto";import "other.proto";
// 客户端protoimport "old.proto";// 当初你能够应用新旧两种包的proto定义了。
通过在编译器命令行参数中应用-I/--proto_pathprotocal 编译器会在指定目录搜寻要导入的文件。如果没有给出标记,编译器会搜寻编译命令被调用的目录。通常你只有指定proto_path标记为你的工程根目录就好。并且指定好导入的正确名称就好。
应用proto2音讯类型
在你的proto3音讯中导入proto2的音讯类型也是能够的,反之亦然,而后proto2枚举不能够间接在proto3的标识符中应用(如果仅仅在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 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; } }}
更新一个音讯类型
如果一个已有的音讯格局已无奈满足新的需要——如,要在音讯中增加一个额定的字段——然而同时旧版本写的代码依然可用。不必放心!更新音讯而不毁坏已有代码是非常简单的。在更新时只有记住以下的规定即可。
- 不要更改任何已有的字段的数值标识。
- 如果你减少新的字段,应用旧格局的字段依然能够被你新产生的代码所解析。你应该记住这些元素的默认值这样你的新代码就能够以适当的形式和旧代码产生的数据交互。类似的,通过新代码产生的音讯也能够被旧代码解析:只不过新的字段会被忽视掉。留神,未被辨认的字段会在反序列化的过程中抛弃掉,所以如果音讯再被传递给新的代码,新的字段仍然是不可用的(这和proto2中的行为是不同的,在proto2中未定义的域仍然会随着音讯被序列化)
- 非required的字段能够移除——只有它们的标识号在新的音讯类型中不再应用(更好的做法可能是重命名那个字段,例如在字段前增加“OBSOLETE_”前缀,那样的话,应用的.proto文件的用户未来就不会无心中从新应用了那些不该应用的标识号)。
- int32, uint32, int64, uint64,和bool是全副兼容的,这意味着能够将这些类型中的一个转换为另外一个,而不会毁坏向前、 向后的兼容性。如果解析进去的数字与对应的类型不相符,那么后果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。
- sint32和sint64是相互兼容的,然而它们与其余整数类型不兼容。
= string和bytes是兼容的——只有bytes是无效的UTF-8编码。 - 嵌套音讯与bytes是兼容的——只有bytes蕴含该音讯的一个编码过的版本。
- fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。
- 枚举类型与int32,uint32,int64和uint64相兼容(留神如果值不相兼容则会被截断),然而在客户端反序列化之后他们可能会有不同的解决形式,例如,未辨认的proto3枚举类型会被保留在音讯中,然而他的示意形式会按照语言而定。int类型的字段总会保留他们的
Any
Any类型音讯容许你在没有指定他们的.proto定义的状况下应用音讯作为一个嵌套类型。一个Any类型包含一个能够被序列化bytes类型的任意音讯,以及一个URL作为一个全局标识符和解析音讯类型。为了应用Any类型,你须要导入import 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<NetworkErrorDetails>()) { NetworkErrorDetails network_error; detail.UnpackTo(&network_error); ... processing network_error ... }}
目前,用于Any类型的动静库仍在开发之中
如果你曾经很相熟proto2语法,应用Any替换扩大。
Oneof
如果你的音讯中有很多可选字段, 并且同时至少一个字段会被设置, 你能够增强这个行为,应用oneof个性节俭内存.
Oneof字段就像可选字段, 除了它们会共享内存, 至少一个字段会被设置。 设置其中一个字段会革除其它字段。 你能够应用case()或者WhichOneof() 办法查看哪个oneof字段被设置, 看你应用什么语言了.
应用Oneof
为了在.proto定义Oneof字段, 你须要在名字后面加上oneof关键字, 比方上面例子的test_oneof:
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; }}
而后你能够减少oneof字段到 oneof 定义中. 你能够减少任意类型的字段, 然而不能应用repeated 关键字.
在产生的代码中, oneof字段领有同样的 getters 和setters, 就像失常的可选字段一样. 也有一个非凡的办法来查看到底那个字段被设置. 你能够在相应的语言API指南中找到oneof API介绍.
Oneof 个性
设置oneof会主动分明其它oneof字段的值. 所以设置屡次后,只有最初一次设置的字段有值.
SampleMessage message;message.set_name("name");CHECK(message.has_name());message.mutable_sub_message(); // Will clear name field.CHECK(!message.has_name());
- 如果解析器遇到同一个oneof中有多个成员,只有最会一个会被解析成音讯。
- oneof不反对repeated.
- 反射API对oneof 字段无效.
如果应用C++,需确保代码不会导致内存透露. 上面的代码会解体, 因为sub_message 曾经通过set_name()删除了
SampleMessage message;SubMessage* sub_message = message.mutable_sub_message();message.set_name("name"); // Will delete sub_messagesub_message->set_... // Crashes here
在C++中,如果你应用Swap()两个oneof音讯,每个音讯,两个音讯将领有对方的值,例如在上面的例子中,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(msg2.has_name());
向后兼容性问题
当减少或者删除oneof字段时肯定要小心. 如果查看oneof的值返回None/NOT_SET, 它意味着oneof字段没有被赋值或者在一个不同的版本中赋值了。 你不会晓得是哪种状况,因为没有方法判断如果未辨认的字段是一个oneof字段。
Tag 重用问题:
- 将字段移入或移除oneof:在音讯被序列号或者解析后,你兴许会失去一些信息(有些字段兴许会被革除)
- 删除一个字段或者退出一个字段:在音讯被序列号或者解析后,这兴许会革除你当初设置的oneof字段
- 拆散或者交融oneof:行为与挪动惯例字段类似。
Map
如果你心愿创立一个关联映射,protocol buffer提供了一种快捷的语法:
map<key_type, value_type> map_field = N;
其中key_type能够是任意Integer或者string类型(所以,除了floating和bytes的任意标量类型都是能够的)value_type能够是任意类型。
例如,如果你心愿创立一个project的映射,每个Projecct应用一个string作为key,你能够像上面这样定义:
map<string, Project> projects = 3;
- Map的字段能够是repeated。
- 序列化后的程序和map迭代器的程序是不确定的,所以你不要冀望以固定程序解决Map
- 当为.proto文件产生生成文本格式的时候,map会依照key 的程序排序,数值化的key会依照数值排序。
- 从序列化中解析或者交融时,如果有反复的key则后一个key不会被应用,当从文本格式中解析map时,如果存在反复的key。
生成map的API当初对于所有proto3反对的语言都可用了,你能够从API指南找到更多信息。
向后兼容性问题
map语法序列化后等同于如下内容,因而即便是不反对map语法的protocol buffer实现也是能够解决你的数据的:
message MapFieldEntry { key_type key = 1; value_type value = 2;}repeated MapFieldEntry map_field = N;
Package
当然能够为.proto文件新增一个可选的package申明符,用来避免不同的音讯类型有命名抵触。如:
package foo.bar;message Open { ... }
在其余的音讯格局定义中能够应用包名+音讯名的形式来定义域的类型,如:
message Foo { ... required foo.bar.Open open = 1; ...}
包的申明符会依据应用语言的不同影响生成的代码。
- 对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中; - 对于Java,包申明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package;
= 对于 Python,这个包申明符是被疏忽的,因为Python模块是依照其在文件系统中的地位进行组织的。 - 对于Go,包能够被用做Go包名称,除非你显式的提供一个option go_package在你的.proto文件中。
- 对于Ruby,生成的类能够被包装在内置的Ruby名称空间中,转换成Ruby所需的大小写款式 (首字母大写;如果第一个符号不是一个字母,则应用PB_前缀),例如Open会在Foo::Bar名称空间中。
- 对于javaNano包会应用Java包,除非你在你的文件中显式的提供一个option java_package。
- 对于C#包能够转换为PascalCase后作为名称空间,除非你在你的文件中显式的提供一个option csharp_namespace,例如,Open会在Foo.Bar名称空间中
包及名称的解析
Protocol buffer语言中类型名称的解析与C++是统一的:首先从最外部开始查找,顺次向外进行,每个包会被看作是其父类包的外部类。当然对于 (foo.bar.Baz)这样以“.”分隔的意味着是从最外围开始的。
ProtocolBuffer编译器会解析.proto文件中定义的所有类型名。 对于不同语言的代码生成器会晓得如何来指向每个具体的类型,即便它们应用了不同的规定。
定义服务(Service)
如果想要将音讯类型用在RPC(近程办法调用)零碎中,能够在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会依据所抉择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具备一个办法,该办法可能接管 SearchRequest并返回一个SearchResponse,此时能够在.proto文件中进行如下定义:
service SearchService { rpc Search (SearchRequest) returns (SearchResponse);}
最直观的应用protocol buffer的RPC零碎是gRPC,一个由谷歌开发的语言和平台中的开源的PRC零碎,gRPC在应用protocl buffer时十分无效,如果应用非凡的protocol buffer插件能够间接为您从.proto文件中产生相干的RPC代码。
如果你不想应用gRPC,也能够应用protocol buffer用于本人的RPC实现,你能够从proto2语言指南中找到更多信息
还有一些第三方开发的PRC实现应用Protocol Buffer。参考第三方插件wiki查看这些实现的列表。
JSON 映射
Proto3 反对JSON的编码标准,使他更容易在不同零碎之间共享数据,在下表中一一形容类型。
如果JSON编码的数据失落或者其自身就是null,这个数据会在解析成protocol buffer的时候被示意成默认值。如果一个字段在protocol buffer中示意为默认值,领会在转化成JSON的时候编码的时候疏忽掉以节俭空间。具体实现能够提供在JSON编码中可选的默认值。
选项
定义.proto文件时可能标注一系列的option。Option并不扭转整个文件申明的含意,但却可能影响特定环境下解决形式。残缺的可用选项能够在google/protobuf/descriptor.proto找到。
一些选项是文件级别的,意味着它能够作用于最外范畴,不蕴含在任何音讯外部、enum或服务定义中。一些选项是音讯级别的,意味着它能够用在音讯定义的外部。当然有些选项能够作用在域、enum类型、enum值、服务类型及服务办法中。到目前为止,并没有一种无效的选项能作用于所有的类型。
如下就是一些罕用的选项:
java_package (文件选项) :这个选项表明生成java类所在的包。如果在.proto文件中没有明确的申明java_package,就采纳默认的包名。当然了,默认形式产生的 java包名并不是最好的形式,依照利用名称倒序形式进行排序的。如果不须要产生java代码,则该选项将不起任何作用。如:
option java_package = "com.example.foo";
java_outer_classname (文件选项): 该选项表明想要生成Java类的名称。如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会依据.proto文件的名称采纳驼峰式的命名形式进行生成。如(foo_bar.proto生成的java类名为FooBar.java),如果不生成java代码,则该选项不起任何作用。如:
option java_outer_classname = "Ponycopter";
optimize_for(文件选项): 能够被设置为 SPEED, CODE_SIZE,或者LITE_RUNTIME。这些值将通过如下的形式影响C++及java代码的生成:
- SPEED (default): protocol buffer编译器将通过在音讯类型上执行序列化、语法分析及其他通用的操作。这种代码是最优的。
- CODE_SIZE: protocol buffer编译器将会产生最大量的类,通过共享或基于反射的代码来实现序列化、语法分析及各种其它操作。采纳该形式产生的代码将比SPEED要少得多, 然而操作要绝对慢些。当然实现的类及其对外的API与SPEED模式都是一样的。这种形式常常用在一些蕴含大量的.proto文件而且并不自觉谋求速度的 利用中。
LITE_RUNTIME: protocol buffer编译器依赖于运行时外围类库来生成代码(即采纳libprotobuf-lite 代替libprotobuf)。这种外围类库因为疏忽了一 些描述符及反射,要比全类库小得多。这种模式常常在挪动手机平台利用多一些。编译器采纳该模式产生的办法实现与SPEED模式并驾齐驱,产生的类通过实现 MessageLite接口,但它仅仅是Messager接口的一个子集。
option optimize_for = CODE_SIZE;
- cc_enable_arenas(文件选项):对于C++产生的代码启用arena allocation
- objc_class_prefix(文件选项):设置Objective-C类的前缀,增加到所有Objective-C从此.proto文件产生的类和枚举类型。没有默认值,所应用的前缀应该是苹果举荐的3-5个大写字符,留神2个字节的前缀是苹果所保留的。
deprecated(字段选项):如果设置为true则示意该字段曾经被废除,并且不应该在新的代码中应用。在大多数语言中没有理论的意义。在java中,这回变成@Deprecated正文,在将来,其余语言的代码生成器兴许会在字标识符中产生废除正文,废除正文会在编译器尝试应用该字段时收回正告。如果字段没有被应用你也不心愿有新用户应用它,尝试应用保留语句替换字段申明。
int32 old_field = 6 [deprecated=true];
自定义选项
ProtocolBuffers容许自定义并应用选项。该性能应该属于一个高级个性,对于大部分人是用不到的。如果你确实心愿创立本人的选项,请参看 Proto2 Language Guide。留神创立自定义选项应用了拓展,拓展只在proto3中可用。
生成拜访类
能够通过定义好的.proto文件来生成Java,Python,C++, Ruby, JavaNano, Objective-C,或者C# 代码,须要基于.proto文件运行protocol buffer编译器protoc。如果你没有装置编译器,下载安装包并遵循README装置。对于Go,你还须要装置一个非凡的代码生成器插件。你能够通过GitHub上的protobuf库找到装置过程
通过如下形式调用protocol编译器:
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 --javanano_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 在目标目录DST_DIR中产生C++代码,能够在C++代码生成参考中查看更多。
- --java_out 在目标目录DST_DIR中产生Java代码,能够在 Java代码生成参考中查看更多。
- --python_out 在目标目录 DST_DIR 中产生Python代码,能够在Python代码生成参考中查看更多。
- --go_out 在目标目录 DST_DIR 中产生Go代码,能够在GO代码生成参考中查看更多。
- --ruby_out在目标目录 DST_DIR 中产生Ruby代码,参考正在制作中。
- --javanano_out在目标目录DST_DIR中生成JavaNano,JavaNano代码生成器有一系列的选项用于定制自定义生成器的输入:你能够通过生成器的README查找更多信息,JavaNano参考正在制作中。
- --objc_out在目标目录DST_DIR中产生Object代码,能够在Objective-C代码生成参考中查看更多。
- --csharp_out在目标目录DST_DIR中产生Object代码,能够在C#代码生成参考中查看更多。
- --php_out在目标目录DST_DIR中产生Object代码,能够在PHP代码生成参考中查看更多。
作为一个不便的拓展,如果DST_DIR以.zip或者.jar结尾,编译器会将输入写到一个ZIP格式文件或者合乎JAR规范的.jar文件中。留神如果输入曾经存在则会被笼罩,编译器还没有智能到能够追加文件。
你必须提议一个或多个.proto文件作为输出,多个.proto文件能够只指定一次。尽管文件门路是绝对于当前目录的,每个文件必须位于其IMPORT_PATH下,以便每个文件能够确定其标准的名称。
原文地址 : https://colobu.com/2017/03/16...
关注 vx
golang技术实验室。获取更多好文
本文由mdnice多平台公布