Protocol Buffer是Google的语言中立的,平台中立的,可扩展机制的,用于序列化结构化数据 - 对比XML,但更小,更快,更简单。您能够定义数据的结构化,而后可使用特殊生成的源代码轻松地在各类数据流中使用各类语言编写和读取结构化数据。php
先来看一个很是简单的例子。假设你想定义一个“搜索请求”的消息格式,每个请求含有一个查询字符串、你感兴趣的查询结果所在的页数,以及每一页多少条查询结果。能够采用以下的方式来定义消息类型的.proto文件了:html
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
复制代码
proto3
语法:若是您不这样作,protobuf 编译器将假定您正在使用proto2。这必须是文件的第一个非空的非注释行。SearchRequest
消息定义指定了三个字段(名称/值对),一个用于要在此类型的消息中包含的每一个数据片断。每一个字段都有一个名称和类型。在上面的示例中,全部字段都是标量类型:两个整数(page_number
和result_per_page
)和一个字符串(query
)。可是,您还能够为字段指定合成类型,包括枚举和其余消息类型。java
正如上述文件格式,在消息定义中,每一个字段都有惟一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不可以再改变。注:[1,15]以内的标识号在编码的时候会占用一个字节。[16,2047]以内的标识号则占用2个字节。因此应该为那些频繁出现的消息元素保留 [1,15]以内的标识号。切记:要为未来有可能添加的、频繁出现的标识号预留一些标识号。python
最小的标识号能够从1开始,最大到2^29 - 1, or 536,870,911。不可使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。若是非要在.proto文件中使用这些预留标识号,编译时就会报警。ios
消息字段能够是如下之一:git
repeated
:此字段能够在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。在proto3中,repeated
数字类型的字段默认使用packed
编码。github
packed
您能够在协议缓冲区编码中找到有关编码的更多信息。golang
能够在单个.proto
文件中定义多种消息类型。若是要定义多个相关消息,这颇有用 - 例如,若是要定义与SearchResponse
消息类型对应的回复消息格式,能够将其添加到相同的消息.proto
:objective-c
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
复制代码
要为.proto
文件添加注释,请使用C / C ++ - 样式//
和/* ... */
语法。json
/ * SearchRequest表示搜索查询,带有分页选项
*代表响应中包含哪些结果。* /
message SearchRequest {
string query = 1;
int32 page_number = 2; //咱们想要哪一个页码?
int32 result_per_page = 3; //每页返回的结果数。
}
复制代码
若是经过彻底删除字段或将其注释来更新消息类型,则将来用户能够在对类型进行本身的更新时重用字段编号。若是之后加载相同的旧版本,这可能会致使严重问题.proto
,包括数据损坏,隐私错误等。确保不会发生这种状况的一种方法是指定已删除字段的字段编号(和/或名称,这也可能致使JSON序列化问题)reserved
。若是未来的任何用户尝试使用这些字段标识符,协议缓冲编译器将会抱怨。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
复制代码
请注意,您不能在同一reserved
语句中混合字段名称和字段编号。
.proto
?当您在a上运行协议缓冲区编译器时.proto
,编译器会生成您所选语言的代码,您须要使用您在文件中描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,并从输入流解析您的消息。
.h
和一个.cc
文件.proto
,并为您文件中描述的每种消息类型提供一个类。.java
文件,其中包含每种消息类型的类,以及Builder
用于建立消息类实例的特殊类。.proto
而后与元类一块儿使用,以在运行时建立必要的Python数据访问类。.pb.go
文件中的每种消息类型生成一个类型的文件。.rb
包含消息类型的Ruby模块的文件。pbobjc.h
和一个pbobjc.m
文件.proto
,其中包含文件中描述的每种消息类型的类。.cs
从每一个文件生成一个文件.proto
,其中包含文件中描述的每种消息类型的类。您能够按照所选语言的教程(即将推出的proto3版本)了解有关为每种语言使用API的更多信息。有关更多API详细信息,请参阅相关API参考(proto3版本即将推出)。
标量消息字段能够具备如下类型之一 - 该表显示.proto
文件中指定的类型,以及自动生成的类中的相应类型:
.proto type | notes | C ++ type | Java type | Python type [2] | Type | Ruby type | C# type | PHP type |
---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | float | double | float | |
float | float | float | float | FLOAT32 | float | float | float | |
INT32 | 使用可变长度编码。编码负数的效率低 - 若是您的字段可能有负值,请改用sint32。 | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
Int64 | 使用可变长度编码。编码负数的效率低 - 若是您的字段可能有负值,请改用sint64。 | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
UINT32 | 使用可变长度编码。 | UINT32 | int [1] | int / long [3] | UINT32 | Fixnum or Bignum (as needed) | UINT | Integer |
UINT64 | 使用可变长度编码。 | UINT64 | Long [1] | int / long [3] | UINT64 | TWINS | ULONG | Integer/string[5] |
SINT32 | 使用可变长度编码。签名的int值。这些比常规int32更有效地编码负数。 | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
sint64 | 使用可变长度编码。签名的int值。这些比常规int64更有效地编码负数。 | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
fixed32 | 老是四个字节。若是值一般大于2 28,则比uint32更有效。 | UINT32 | int [1] | int / long [3] | UINT32 | Fixnum or Bignum (as needed) | UINT | Integer |
fixed64 | 老是八个字节。若是值一般大于2 56,则比uint64更有效。 | UINT64 | Long [1] | int / long [3] | UINT64 | TWINS | ULONG | Integer/string[5] |
sfixed32 | 老是四个字节。 | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
sfixed64 | 老是八个字节。 | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
Boolean | Boolean | Boolean | Boolean | Boolean | TrueClass / FalseClass | Boolean | Boolean | |
string | 字符串必须始终包含UTF-8编码或7位ASCII文本。 | string | string | str / unicode[4] | string | String (UTF-8) | string | string |
byte | 能够包含任意字节序列。 | string | Byte string | Strait | []byte | String (ASCII-8BIT) | Byte string | string |
在协议缓冲区编码中序列化消息时,您能够找到有关如何编码这些类型的更多信息。
[1]在Java中,无符号的32位和64位整数使用它们的带符号对应表示,最高位只是存储在符号位中。
[2]在全部状况下,将值设置为字段将执行类型检查以确保其有效。
[3] 64位或无符号32位整数在解码时始终表示为long,但若是在设置字段时给出int,则能够为int。在全部状况下,该值必须适合设置时表示的类型。见[2]。
[4] Python字符串在解码时表示为unicode,但若是给出了ASCII字符串,则能够是str(这可能会发生变化)。
[5] Integer用于64位计算机,字符串用于32位计算机。
解析消息时,若是编码消息不包含特定的单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:
重复字段的默认值为空(一般是相应语言的空列表)。
请注意,对于标量消息字段,一旦解析了消息,就没法肯定字段是否显式设置为默认值(例如,是否设置了布尔值false
)或者根本没有设置:您应该记住这一点在定义消息类型时。例如,false
若是您不但愿默认状况下也发生这种行为,那么在设置为时,没有一个布尔值能够启用某些行为。还要注意的是,若是一个标消息字段被设置为默认值,该值将不会在电线上连载。
有关默认值如何在生成的代码中工做的更多详细信息,请参阅所选语言的生成代码指南。
在定义消息类型时,您可能但愿其中一个字段只有一个预约义的值列表。例如,假设你想添加一个 corpus
字段每一个SearchRequest
,其中语料库能够 UNIVERSAL
,WEB
,IMAGES
,LOCAL
,NEWS
,PRODUCTS
或VIDEO
。您能够很是简单地经过enum
为每一个可能的值添加一个常量来定义消息定义。
在下面的示例中,咱们添加了一个带有全部可能值的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
枚举的第一个常量映射为零:每一个枚举定义必须包含一个映射到零的常量做为其第一个元素。这是由于:
您能够经过为不一样的枚举常量指定相同的值来定义别名。为此,您须要将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
值在线上使用varint编码,所以负值效率低,所以不建议使用。您能够enum
在消息定义中定义s,如上例所示,enum
也能够在外部定义 - 这些能够在.proto
文件的任何消息定义中重用。您还可使用enum
语法将一个消息中声明的类型用做另外一个消息中的字段类型。 *MessageType*.*EnumType*
当你在.proto
使用a的协议缓冲编译器上运行时enum
,生成的代码将具备enum
Java或C ++ 的相应代码,这EnumDescriptor
是Python的一个特殊类,用于在运行时生成的类中建立一组带有整数值的符号常量。
在反序列化期间,将在消息中保留没法识别的枚举值,可是当反序列化消息时,如何表示这种值取决于语言。在支持具备超出指定符号范围的值的开放枚举类型的语言中,例如C ++和Go,未知的枚举值仅做为其基础整数表示存储。在具备封闭枚举类型(如Java)的语言中,枚举中的大小写用于表示没法识别的值,而且可使用特殊访问器访问基础整数。在任何一种状况下,若是消息被序列化,则仍然会使用消息序列化没法识别的值。
有关如何enum
在应用程序中使用消息的详细信息,请参阅所选语言的生成代码指南。
若是经过彻底删除枚举条目或将其注释掉来更新枚举类型,则将来用户能够在对类型进行本身的更新时重用该数值。若是之后加载相同的旧版本,这可能会致使严重问题.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;
}
复制代码
在上面的示例中,Result
消息类型在同一文件中定义SearchResponse
- 若是要用做字段类型的消息类型已在另外一个.proto
文件中定义,该怎么办?
您能够.proto
经过导入来使用其余文件中的定义。要导入其余.proto
人的定义,请在文件顶部添加import语句:
import“myproject / other_protos.proto”;
复制代码
默认状况下,您只能使用直接导入.proto
文件中的定义。可是,有时您可能须要将.proto
文件移动到新位置。.proto
如今,您能够.proto
在旧位置放置一个虚拟文件,以使用该import public
概念将全部导入转发到新位置,而不是直接移动文件并在一次更改中更新全部调用站点。import public
任何导入包含该import public
语句的proto的人均可以传递依赖关系。例如:
// new.proto
// All definitions are moved here
复制代码
// old.proto
//This is the proto that all clients are importing.
import public“new.proto”;
import“other.proto”;
复制代码
// client.proto
import "old.proto";
//您使用old.proto和new.proto中的定义,但不使用other.proto
复制代码
协议编译器使用-I
/ --proto_path
flag 在协议编译器命令行中指定的一组目录中搜索导入的文件 。若是没有给出标志,它将查找调用编译器的目录。一般,您应该将--proto_path
标志设置为项目的根目录,并对全部导入使用彻底限定名称。
能够导入proto2消息类型并在proto3消息中使用它们,反之亦然。可是,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;
}
}
}
复制代码
若是现有的消息类型再也不知足您的全部需求 - 例如,您但愿消息格式具备额外的字段 - 但您仍然但愿使用使用旧格式建立的代码,请不要担忧!在不破坏任何现有代码的状况下更新消息类型很是简单。请记住如下规则:
.proto
不会意外地重复使用该编号。int32
,uint32
,int64
,uint64
,和bool
都是兼容的-这意味着你能够改变这些类型到另外一个的一个场不破坏forwards-或向后兼容。若是从导线中解析出一个不符合相应类型的数字,您将得到与在C ++中将该数字转换为该类型相同的效果(例如,若是将64位数字做为int32读取,它将被截断为32位)。sint32
而且sint64
彼此兼容但与其余整数类型不兼容。string``bytes
只要字节是有效的UTF-8 ,它们是兼容的。bytes
若是字节包含消息的编码版本,则嵌入消息是兼容的。fixed32
与兼容sfixed32
,并fixed64
用sfixed64
。enum
与兼容int32
,uint32
,int64
,和uint64
电线格式条款(注意,若是他们不适合的值将被截断)。但请注意,在反序列化消息时,客户端代码可能会以不一样方式对待它们:例如,enum
将在消息中保留未识别的proto3 类型,但在反序列化消息时如何表示这种类型取决于语言。Int字段老是保留它们的价值。oneof
是安全且二进制兼容的。oneof
若是您肯定没有代码一次设置多个字段,则将多个字段移动到新字段多是安全的。将任何字段移动到现有字段oneof
并不安全。未知字段是格式良好的协议缓冲区序列化数据,表示解析器没法识别的字段。例如,当旧二进制文件解析具备新字段的新二进制文件发送的数据时,这些新字段将成为旧二进制文件中的未知字段。
最初,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*
不一样的语言实现将支持运行时库佣工类型安全的方式打包和解包的任何值-例如,在Java中,任何类型都会有特殊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类型的运行时库。
若是您有一个包含许多字段的消息,而且最多只能同时设置一个字段,则可使用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字段与常规字段具备相同的getter和setter。您还可使用特殊方法检查oneof中的值(若是有)。您能够在相关API参考中找到有关所选语言的oneof API的更多信息。
设置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的多个成员,则在解析的消息中仅使用看到的最后一个成员。
一个不可能repeated
。
Reflection API适用于其中一个字段。
若是您使用的是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
复制代码
一样在C ++中,若是你有Swap()
两个消息与oneofs,每一个消息最终将与另外一个消息结果:在下面的例子中,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返回的值None
/ NOT_SET
,这可能意味着oneof还没有设置或已在不一样版本的oneof的被设置为一个字段。没有办法区分,由于没有办法知道线上的未知字段是不是其中一个成员。
若是要在数据定义中建立关联映射,协议缓冲区提供了一种方便的快捷方式语法:
map < key_type ,value_type > map_field = N ;
复制代码
...其中key_type
能够是任何整数或字符串类型(所以,除了浮点类型以外的任何标量类型bytes
)。请注意,枚举不是有效的key_type
。的value_type
能够是任何类型的除另外一地图。
所以,例如,若是要建立项目映射,其中每条Project
消息都与字符串键相关联,则能够像下面这样定义它:
map < string ,Project > projects = 3 ;
复制代码
repeated
。.proto
,地图按键排序。数字键按数字排序。生成的地图API目前可用于全部proto3支持的语言。您能够在相关API参考中找到有关所选语言的地图API的更多信息。
映射语法在线上等效于如下内容,所以不支持映射的协议缓冲区实现仍可处理您的数据:
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
复制代码
任何支持映射的协议缓冲区实现都必须生成和接受上述定义能够接受的数据。
您能够向.proto
文件添加package
可选说明符,以防止协议消息类型之间的名称冲突。
package foo.bar;
message Open { ... }
复制代码
而后,您能够在定义消息类型的字段时使用包说明符:
message Foo {
...
foo.bar.Open open = 1;
...
}
复制代码
包说明符影响生成的代码的方式取决于您选择的语言:
Open
将在命名空间中foo::bar
。option java_package
在.proto
文件中明确提供了该包。option go_package
在.proto
文件中明确提供。PB_
则前置)。例如,Open
将在命名空间中Foo::Bar
。option csharp_namespace
在.proto
文件中明确提供。例如,Open
将在命名空间中Foo.Bar
。协议缓冲区语言中的类型名称解析与C ++相似:首先搜索最里面的范围,而后搜索下一个范围,依此类推,每一个包被认为是其父包的“内部”。一个领先的'。' (例如,.foo.bar.Baz
)意味着从最外层的范围开始。
protobuf 编译器经过解析导入的.proto
文件来解析全部类型名称。每种语言的代码生成器都知道如何使用该语言引用每种类型,即便它具备不一样的范围规则。
若是要将消息类型与RPC(远程过程调用)系统一块儿使用,则能够在.proto
文件中定义RPC服务接口,protobuf 编译器将使用您选择的语言生成服务接口代码和存根。所以,例如,若是要定义RPC服务请求方法为:SearchRequest
和返回方法为:SearchResponse
,能够.proto
按以下方式在文件中定义它:
service SearchService {
rpc Search(SearchRequest)returns(SearchResponse);
}
复制代码
与协议缓冲区一块儿使用的最简单的RPC系统是gRPC:一种由Google开发的,平台中立的开源RPC系统。gRPC特别适用于protobuf,并容许在您的.proto
文件中使用特殊的protobuf 编译器插件直接生成相关的RPC代码。
若是您不想使用gRPC,也能够将protobuf与您本身的RPC实现一块儿使用。您能够在Proto2语言指南中找到更多相关信息。
还有一些正在进行的第三方项目使用Protocol Buffers开发RPC实现。有关咱们了解的项目的连接列表,请参阅第三方加载项wiki页面。
Proto3支持JSON中的规范编码,使得在系统之间共享数据变得更加容易。在下表中逐个类型地描述编码。
若是JSON编码数据中缺乏值null
,或者其值为,则在解析为协议缓冲区时,它将被解释为适当的默认值。若是字段在协议缓冲区中具备默认值,则默认状况下将在JSON编码数据中省略该字段以节省空间。实现能够提供用于在JSON编码的输出中发出具备默认值的字段的选项。
proto3 | JSON | JSON示例 | 笔记 |
---|---|---|---|
message | object | {"fooBar": v, "g": null,…} |
生成JSON对象。消息字段名称映射到小写驼峰并成为JSON对象键。若是json_name 指定了field选项,则指定的值将用做键。解析器接受小写驼峰名称(或json_name 选项指定的名称)和原始proto字段名称。null 是全部字段类型的可接受值,并将其视为相应字段类型的默认值。 |
eunm | String | "FOO_BAR" |
使用proto中指定的枚举值的名称。解析器接受枚举名称和整数值。 |
map<K,V> | 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 | string | 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”。接受数字或字符串。指数表示法也被接受。 |
any | object | {"@type": "url", "f": v, … } |
若是Any包含具备特殊JSON映射的值,则将按以下方式进行转换:。不然,该值将转换为JSON对象,并将插入该字段以指示实际的数据类型。{"@type": xxx, "value": yyy}``"@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" |
见。field_mask.proto |
ListValue | array | [foo, bar, …] |
|
Value | value | 任何JSON值 | |
NullValue | null | JSON null |
proto3 JSON实现能够提供如下选项:
.proto
文件中的各个声明可使用许多选项进行注释。选项不会更改声明的总体含义,但可能会影响在特定上下文中处理它的方式。可用选项的完整列表在中定义google/protobuf/descriptor.proto
。
一些选项是文件级选项,这意味着它们应该在顶级范围内编写,而不是在任何消息,枚举或服务定义中。一些选项是消息级选项,这意味着它们应该写在消息定义中。一些选项是字段级选项,这意味着它们应该写在字段定义中。选项也能够写在枚举类型,枚举值,服务类型和服务方法上; 可是,目前没有任何有用的选择。
如下是一些最经常使用的选项:
java_package
(文件选项):用于生成的Java类的包。若是.proto
文件中没有给出显式选项java_package
,则默认状况下将使用proto包(使用文件中的“package”关键字指定 .proto )。可是,proto包一般不能生成好的Java包,由于proto包不会以反向域名开头。若是不生成Java代码,则此选项无效。
option java_package =“com.example.foo”;
复制代码
java_multiple_files
(文件选项):致使在包级别定义顶级消息,枚举和服务,而不是在.proto文件以后命名的外部类中。
option java_multiple_files = true;
复制代码
java_outer_classname
(file option):要生成的最外层Java类(以及文件名)的类名。若是 .proto
文件中没有指定 java_outer_classname
,则经过将.proto
文件名转换为驼峰格式(所以 foo_bar.proto
成为FooBar.java
)来构造类名。若是不生成Java代码,则此选项无效。option java_outer_classname =“Ponycopter”;
复制代码
optimize_for
(文件选项):能够设置为SPEED
,CODE_SIZE
或LITE_RUNTIME
。这会如下列方式影响C ++和Java代码生成器(可能还有第三方生成器):
SPEED
(默认值):protobuf 编译器将生成用于对消息类型进行序列化,解析和执行其余常见操做的代码。此代码通过高度优化。CODE_SIZE
:protobuf 编译器将生成最少的类,并依赖于基于反射的共享代码来实现序列化,解析和各类其余操做。所以生成的代码比使用SPEED
小得多,但操做会更慢。类仍将实现与SPEED
模式彻底相同的公共API 。此模式在包含很是大数量的.proto
文件的应用程序中最有用,而且不须要全部文件都很是快速。LITE_RUNTIME
:protobuf 编译器将生成仅依赖于“lite”运行时库(libprotobuf-lite
而不是libprotobuf
)的类。精简版运行时比整个库小得多(大约小一个数量级),但省略了描述符和反射等特定功能。这对于在移动电话等受限平台上运行的应用程序尤为有用。编译器仍然会像在SPEED
模式中同样生成全部方法的快速实现。生成的类将仅实现MessageLite
每种语言的接口,该接口仅提供完整Message
接口的方法的子集。option optimize_for = CODE_SIZE;
复制代码
cc_enable_arenas
(文件选项):为C ++生成的代码启用竞技场分配。
objc_class_prefix
(文件选项):设置Objective-C类前缀,该前缀预先添加到此.proto的全部Objective-C生成的类和枚举中。没有默认值。您应该使用Apple建议的 3-5个大写字符之间的前缀。请注意,Apple保留全部2个字母的前缀。
deprecated
(字段选项):若是设置为true
,则表示该字段已弃用,新代码不该使用该字段。在大多数语言中,这没有实际效果。在Java中,这成为一个@Deprecated
注释。未来,其余特定于语言的代码生成器可能会在字段的访问器上生成弃用注释,这将致使在编译尝试使用该字段的代码时发出警告。若是任何人都没有使用该字段,而且您但愿阻止新用户使用该字段,请考虑使用保留语句替换字段声明。
int32 old_field = 6 [deprecated = true];
复制代码
Protocol Buffers还容许您定义和使用本身的选项。这是大多数人不须要的高级功能。若是您确实认为须要建立本身的选项,请参阅Proto2语言指南以获取详细信息。请注意,建立自定义选项使用的扩展名仅容许用于proto3中的自定义选项。
根据实际工做须要,生成如下对应语言的自定义消息类型Java,Python,C ++,Go, Ruby, Objective-C,或C#的.proto
文件,你须要运行protobuf 编译器protoc
上.proto
。若是还没有安装编译器,请下载该软件包并按照自述文件中的说明进行操做。对于Go,您还须要为编译器安装一个特殊的代码生成器插件:您能够在GitHub上的golang / protobuf存储库中找到这个和安装说明。
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
以便编译器能够肯定其规范名称。