本文描述如何使用proto3语法去构造你的数据结构,对官方文档不彻底译文,只是摘出本人须要的部分来简单翻译官网地址,若是你没法进入官网连接请自行"跳墙"-_-.java
让咱们先看一个 proto3 的查找请求参数的消息格式的例子,这个请求参数例子模仿分页查找请求,他有一个请求参数字符串,有一个当前页的参数还有一个每页返回数据大小的参数,proto文件内容以下:c++
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
复制代码
第一行的含义是限定该文件使用的是proto3的语法,若是没有 syntax = "proto3";
编程
SearchRequest定义有三个承载消息的属性,每个被定义在SearchRequest消息体中的字段,都是由数据类型和属性名称组成。
bash
在上面的例子中,全部的属性都是标量,两个整型(page_number、result_per_page)和一个字符串(query),你还能够在指定复合类型,包括枚举类型或者其余的消息类型。数据结构
就像所看见的同样,每个被定义在消息中的字段都会被分配给一个惟一的标量,这些标量用于标识你定义在二进制消息格式中的属性,标量一旦被定义就不容许在使用过程当中再次被改变。标量的值在1~15的这个范围里占一个字节编码(详情请参看 谷歌的 Protocol Buffer Encoding )。编程语言
消息属性规则以下:
ui
singular: 一个正确的消息能够有零个或者多个这样的消息属性(可是不要超过一个).
repeated: 这个属性能够在一个正确的消息格式中重复任意次数(包括零次),
在一个proto文件中能够定义多个消息类型,你能够在一个文件中定义一些相关的消息类型,上面的例子proto文件中只有一个请求查找的消息类型,如今能够为他多添加一个响应的消息类型,具体以下:google
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
....
}
复制代码
proto文件中的注释使用的是c/c++中的单行注释 //
语法风格。
以下:编码
message SearchRequest {
string query = 1;
int32 page_number = 2; // 当前页数
int32 result_per_page = 3; // 每页数据返回的数据量
复制代码
为了不在加载相同的.proto的旧版本,包括数据损坏,隐含的错误等,这可能会致使严重的问题的方法是指定删除的字段的字段标签(和/或名称,也可能致使JSON序列化的问题)被保留。 若是未来的用户尝试使用这些字段标识符,协议缓冲区编译器将会报错。
保留字段的使用例子:url
message Foo {
reserved 2;
reserved "foo", "bar";
}
复制代码
上述例子定义保留属性为"foo", "bar"
,定义保留属性位置为2,即在2这个位置上不能够定义属性,如:string name=2;
是不容许的,编译器在编译proto文件的时候若是发现,2这个位置上有属性被定义则会报错。
一个信息标量具备以下表格所示的数据类型,下表主要是对.proto文件的值类型和java的值类型的对照表
.proto Type | Java Type |
---|---|
double | double |
float | float |
int32 | int |
int64 | long |
uint32 | int |
uint64 | long |
sint32 | int |
sint64 | long |
fixed32 | int |
fixed64 | long |
sfixed32 | int |
sfixed64 | long |
bool | boolean |
string | String |
bytes | ByteString |
当proto消息被解析成具体的语言的时候,若是消息编码没包含特定的元素,则消息对象中的属性会被设置默认值,这些默认值具体以下:
string
类型,默认值是空字符串,注意不是nullbytes
类型,默认值是空bytesbool
类型,默认值是false数字
类型,默认值是0枚举
类型,默认值是第一个枚举值,即0repeated
修饰的属性,默认值是空(在相对应的编程语言中一般是一个空的list).proto容许你在定义的消息类型的时候定义枚举类型,以下例,在消息类型中定义并使用枚举类型:
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做为默认值
第一个元素的值取0,用于与第一个元素枚举值做为默认值的proto2语义兼容
allow_alias
选项,并将值设置为true便可,若是没有设置该值就是用别名,在编译的时候会报错。enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
//若是解除这个注释编译器在编译该proto文的时候会报错
// RUNNING = 1;
}
复制代码
proto支持的枚举值的范围是32位的整形,即Java 中的int类型,其余请参看官网。
你能够在定义消息类型的时候饮用其余已经定义好的消息类型做为新消息类型的属性,官网例子以下:
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
复制代码
在上面的消息例子中,SearchResponse这个响应消息类型的属性results,返回的是一个Result类型的消息列表。
在上面的例子中,Result和SearchResponse消息类型被定义在同一个.proto文件中,若是把他们分红两个文件定义,应该如何引用呢?
proto中为咱们提供了import
关键字用于引入不一样.proto
文件中的消息类型,你能够在你的.proto
文件的顶部加入以下语句由于其余.proto
文件的消息类型:
import "myproject/other_protos.proto";
例子:
search_response.proto
syntax = "proto3";
import "test/result.proto";
package test1;
message SearchResponse {
//包名.消息名
repeated test2.Result results = 1;
}
复制代码
result.proto,在与search_response.proto同级目录的test下
syntax = "proto3";
package test2;
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
复制代码
若是两个.proto文件在同一个目录下直接这样import "result.proto";
倒入便可。
咱们还能够在消息类型中定义消息,例子以下:
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
复制代码
在上面的例子中在SearchResponse消息体中定义了一个Result消息并使用。
若是想在其余的消息体引用Result这个消息,能够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支持map属性类型的定义,语法以下:
map<key_type,value_type> map_field = N;
key_type能够是任何整数或字符串类型(除浮点类型和字节以外的任何标量类型,枚举类型也是不合法的key类型),value_type能够是任何类型的数据。
map更具体的使用方式参看API
能够为proto
文件指定包名,防止消息命名冲突。
例子以下:
package foo.bar;
message Open { ... }
复制代码
当你在为消息类型定义属性的时候,你能够经过命名.类型
的形式来使用已经定义好的消息类型,以下:
Message Foo {
...
foo.bar.Open open = 1;
...
}
复制代码
若是你想在RPC中使用已经定义好的消息类型,你能够在.proto
文件中定一个消息服务接口,protocol buffer编译器会生成对应语言的接口代码。
接口定义例子:
service SearchService {
// 方法名 方法参数 返回值
rpc Search(SearchRequest) returns (SearchResponse);
}
复制代码
下面只列出java的.proto
文件经常使用的一下选贤,其余选项前参看官网文档
java_package
(文件选项):指定生成的java类所在的包, 若是在.proto文件中没有提供明确的java_package
选项,那么默认状况下,将使用proto
包。若是没有生成java代码该选项默认是不生效的。
option java_package = "org.example.foo";
java_multiple_files
(文件选项):指定在proto文件中定义的全部消息、枚举和服务在生成java类的时候都会生成对应的java类文件,而不是之内部类的形式出现。
option java_multiple_files = true;
java_outer_classname
(文件选项):指定生成的java类文件名称,若是不指定则会默认使用.proto
文件的文件名称,若是没有生成java类文件,则该选项不会生效 <span id="1">Hello World</span>。
option java_outer_classname = "HelloWorld";