本文描述如何使用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";