1.说明
此向导介绍如何使用protocol buffer language创建一个自己的protocolbuffer文件,包括语法与如何通过“.proto”文件生成数据访问的类,此处只介绍proto2,proto3的更多消息点这里。
这是一个参考指南,一步一步功能描述的示例,请访问以下链接,并选择你自己熟悉的开发语言。
2.定义消息类型
首先我们来看一个简单的示例,定义一个searchrequest消息格式,每一个search request有一个query字符串,页码,每页结果数量。以下是定义的“.proto”文件:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
消息指定了三“段”(“名-值”对),每一段,有修饰符、类型、名称、编号组成,还会有一些可选项组成,如指定缺省值呀,后续章节中会介绍到。
段类型
在以上的示例中,所有的段都指定了类型。你也可以用复合类型,包括枚举与其他消息类型(protobuffer 定义的类型)。
分配标记
从以上示例中可以看到,每一段都指定了唯一编号“= x”,它用于二进制格式中标记“段”,当你的消息类型投入使用后,它们的顺序不能改变。编码时编号在1~15区间内的编号占用一个字节,在16~2047区间用两个字节,所以,你可以保留1~15的编号给那些比较常用的元素使用,并为将来可能要增加的段预留一些此区间的编号。
最小编号为1,最大编号为229 - 1,或者536,870,911,但是19000 ~ 19999区间的编号是保留给Protocol Buffers使用的。
修饰符
required: 一个格式完好的消息必须最少有一个这种类型的段。被这种修饰符修饰的段,是必须赋值的,否则会被认为“未初始化”,如果未赋值,在debug版本序列化时会抛出断言错误,release版本能顺利通过,但是反序列化(解析)时,必然会失败的。除此之外,required与optional修饰类型就没有什么区别了。
optional: 一个格式完好的消息有N(N≥0)个这种类型的段。对于此字段的赋值,不是必须的。如果没有赋值,它将使用默认值,对于缺省数据类型,你可以指定默认值,如伪代码中的phone number,如果没有指定默认值,将使用系统默认值,数字类型为0,string为空,bool型为false。对于嵌套类型,默认值为“默认实例”或“原型”。
repeated: 字段会出现N(≥0)次,重复的值将按顺序保存在“protocol buffer”中,你只要把它当成一个动态数组即可。
由于历史原因,repeated修饰的段的数据类型如果是数字类型的话,不能高效编码,为提高效果可以使用一个选项[packed=true]来获得更高的效率,示例如:
repeated int32samples = 4 [packed=true];
关于packed参见这里。
Required 是永久的,使用此种修饰符时,要特别小心,当你不想给此种类型的字段赋值的话,你需要把它改成Optional类型,它可能会出现一些问题----接受方可能会认为此消息是非完事的,而拒绝解析。有些google开发者认为required利大于弊,所以他们更喜欢使用optional与repeated。当然,这种观点不一定是普遍的。
定义多个消息
可以在一个“.proto”文件定义多个消息,特别是对那些有相互关联的消息,比较适用。如你需要给以上示例的请求消息加一个响应消息
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
message SearchResponse {
...
}
关于注释
“.proto”文件注释,使用的是C/C++语法“//”,如下:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;// Which page number do we want?
optional int32 result_per_page = 3;
// Number of results to return per page.
}
保留段
如果你要对以前定义的消息中的段删除,或者注释。将来使用者可能会更新他们的消息,并重新使用这些段,或者他们又使用此消息的旧版本,这将导致数据损坏,隐性错误等问题,有一个办法可以避免这些问题。把这个删除的段指定为reserved类型,可以通过它的标志指定,也可以通过名称(JSON版本会有问题)指定,指定后使用都如果再使用这些段,将会收到错误提醒。使用reserved时,同一行,不能混合使用标志与名称。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
.proto
生成的内容
通过protocol buffer 编译器对.proto文件进行编译后,能生成你选择的语言的代码。你可以通过此代码对你在.proto文件中描述的数据进行提取、给段赋值、把你打包后的数据序列化成流、把接收到的流反序列化成类实例等操作。
C++:对应每一个.proto文件生成.h与.cpp文件。每个消息将生成一个类。可以通过此链接,找到对应语言的API。
3.数据类型
以下列表中是.proto文件中数据类型与相应的语言之间的数据类型的对应关系。
.proto Type |
Notes |
C++ Type |
Java Type |
Python Type[2] |
Go Type |
double |
double |
double |
float |
*float64 |
|
float |
float |
float |
float |
*float32 |
|
int32 |
Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. |
int32 |
int |
int |
*int32 |
int64 |
Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. |
int64 |
long |
int/long[3] |
*int64 |
uint32 |
Uses variable-length encoding. |
uint32 |
int[1] |
int/long[3] |
*uint32 |
uint64 |
Uses variable-length encoding. |
uint64 |
long[1] |
int/long[3] |
*uint64 |
sint32 |
Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. |
int32 |
int |
int |
*int32 |
sint64 |
Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. |
int64 |
long |
int/long[3] |
*int64 |
fixed32 |
Always four bytes. More efficient than uint32 if values are often greater than 228. |
uint32 |
int[1] |
int |
*uint32 |
fixed64 |
Always eight bytes. More efficient than uint64 if values are often greater than 256. |
uint64 |
long[1] |
int/long[3] |
*uint64 |
sfixed32 |
Always four bytes. |
int32 |
int |
int |
*int32 |
sfixed64 |
Always eight bytes. |
int64 |
long |
int/long[3] |
*int64 |
bool |
bool |
boolean |
bool |
*bool |
|
string |
A string must always contain UTF-8 encoded or 7-bit ASCII text. |
string |
String |
str/unicode[4] |
*string |
bytes |
May contain any arbitrary sequence of bytes. |
string |
ByteString |
str |
[]byte |
关于以上数据类型的编码方式的详情,点击这里。
[1]在Java中, unsigned 32-bit and64-bit被解释成有符合整形,最高位被描述成符号位。
[2]对段进行赋值时,会执行类型检查。
[3]64-bit orunsigned 32-bit 整形被在解析时都被解析成long型,可以为int型,如果在设置的时候设置成int型的话。总之,值必须与设置的时候一致。参见[2]。
[4]Pythonstrings将解析为宽字符,同时可以是ASCIIÿ