Protocol Buffers(Protobuf) 官方文檔--Protobuf語言指南

Protocol Buffers(Protobuf) 官方文檔--Protobuf語言指南html

約定:爲方便書寫,ProtocolBuffers在下文中將已Protobuf代替。java

本指南將向您描述如何使用protobuf定義i結構化Protobuf數據,包括.proto文件語法和如何使用.proto文件生成數據存取類。python

做爲一個參考指南,本文檔將以示例的形式一步步向您介紹Protobuf的特色。您能夠參考您所選擇的語言的示例。tutorial 編程

--------------------------------------小小的分割線-----------------------------------------數組

定義一個消息類型ide

首先,看一個很是簡單的例子,好比說你想定義一個 搜索請求消息 ,每一個搜索請求都有一個 查詢的字符串(關鍵字:好比咱們上百度搜索 《報告老闆》),和咱們搜索出來的一個感興趣的網頁,以及搜索到的全部網頁總數。 來看看這個.proto文件是如何定義的。ui

 

1 message SearchRequest {
2   required string query = 1;
3   optional int32 page_number = 2;
4   optional int32 result_per_page = 3;
5 }

這個"搜索請求"消息指定了三個字段(名稱/屬性 組合),每個你想要包含在這類型的信息內的東西,都必須有一個字段,每一個字段有一個名稱和類型!google

指定字段類型

在上面的示例中,全部的字段都是標量類型(scalar types):兩個整數(integers:page_number 和 result_per_page)和一個字符串(string:query:查詢的關鍵字),不過你能夠在你的字段內指定符合類型。包括枚舉類型(enumerations)和其餘的消息類型編碼

分配指定標籤號

如你所見,每一個消息的字段都有一個惟一的數字標籤,這些標籤用來表示你的字段在二進制消息(message binary format)中處的位置。而且一旦指定標籤號,在使用過程當中是不能夠更改的,標記這些標籤號在1-15的範圍內每一個字段須要使用1個字節用來編碼這一個字節包括字段所在的位置和字段的類型!(須要更多關於編碼的信息請點擊Protocol Buffer Encoding)。標籤號在16-2047須要使用2個字節來編碼。因此你最好將1-15的標籤號爲頻繁使用到的字段所保留。若是未來可能會添加一些頻繁使用到的元素,記得留下一些1-15標籤號。spa

最小可指定的標籤號爲1,最大的標籤號爲229 - 1或者536870911。不能使用19000-19999的標籤號(FieldDescriptor::kFirstReservedNumber 至 FieldDescriptor::kLastReservedNumber) 這些標籤號是爲protobuf內部實現所保留的,若是你在.proto文件內使用了這些標籤號Protobuf編譯器將會報錯!

 

指定字段規則

消息字段能夠被指定爲如下三種:

  • required: 完整的消息內必須擁有此字段。此字段是必須擁有的 (雙方都要有)
  • optional: 完整的消息內此字段是可選的,可擁有也能夠沒有(雙方可選)
  • repeated: 完整的消息內本字段的值能夠擁有任意個,重複的值的次數會保存下來。(雙方可選,數組)

由於歷史的緣由:repeated字段若是是基本的數字類型的話會沒法編碼。新的代碼應該使用特殊的關鍵字[packed=true] 來使其獲得有效的編碼.例如

[cpp]  view plain copy print ?
 
  1. repeated int32 samples = 4 [packed=true];  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. repeated int32 samples = 4 [packed=true];  

注意:你應該當心將字段設置爲required,若是你但願在某些狀況下取消required字段的讀寫,它將改變字段爲optional屬性,舊的的讀取方將會認爲此消息不徹底。可能會無心的將其丟棄。你應該考慮自定義一個消息檢查程序。google的一些工程師認爲使用optinal字段的好處大於required。可是顯然這個觀點並非通用的。

添加更多的消息類型

多個消息類型能夠定義在同一個.proto文件內,這對定義多個有關聯的消息是是十分有用的。例如,若是你想定義一個用於回覆SearchResponse消息,你能夠像這樣在.proto內添加。

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3;  
  5. }  
  6.   
  7. message SearchResponse {  
  8.  ...  
  9. }  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3;  
  5. }  
  6.   
  7. message SearchResponse {  
  8.  ...  
  9. }  

 

添加註釋

添加註釋的方式和C/C++是同樣的。使用//

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;// Which page number do we want?  
  4.   optional int32 result_per_page = 3;// Number of results to return per page.  
  5. }  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;// Which page number do we want?  
  4.   optional int32 result_per_page = 3;// Number of results to return per page.  
  5. }  

 

.proto文件會生成什麼?

當你使用protobuf編譯器編譯一個.proto文件,它會生成在.proto內你描述的消息類型的操做代碼,這些代碼是根據你所選擇的編程功能語言決定的。這些操做代碼內包含了設置字段值 和讀取字段值,以及序列化到輸出流 和 從輸入流反序列化。

C++:編譯器會按照每一個.proto文件生成與其對應的.h和.cc文件,每一個消息相似都有獨立的消息操做類。

Java:編譯器將會生成一個.java文件和一個操做類,此操做類爲全部消息類型所共有, 使用一個特別的Builder類爲每一個消息類型實例化.

Python:有一點不一樣 – 編譯器會爲每一個消息生成一個模塊每一個模塊有一個靜態描述符, 該模塊與一個元類在運行時建立一個須要的數據操做類。

從你所選擇語言的例程,你能夠找到更多關於API的內容, 須要關於API的詳細信息, 參考: API reference.

標量值類型

一個消息的字段若是要使用標量可以使之爲如下類型 –這個表格顯示了在.proto文件內能夠指定的類型, 與自動生成的相對類型!

.proto Type Notes C++ Type Java Type Python Type[2]
double   double double float
float   float float float
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
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]
uint32 Uses variable-length encoding. uint32 int[1] int/long[3]
uint64 Uses variable-length encoding. uint64 long[1] int/long[3]
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long[3]
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int[1] int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long[1] int/long[3]
sfixed32 Always four bytes. int32 int int
sfixed64 Always eight bytes. int64 long int/long[3]
bool   bool boolean boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode[4]
bytes May contain any arbitrary sequence of bytes. string ByteString str

 

你能夠在Protocol Buffer Encoding.找到更多關於.這些類型如何編碼,如何序列化定義消息的信息!

[1] 在Java中, 無符號32位和64位整數與其有符號相對應, 最高位用來保存符號!

[2] 在全部狀況下, 設置某個字段的值將會執行類型檢查確保其值是合法的!

[3] 64位或32位無符號整數在解碼中會以long來解碼, 給字段賦值的時候能夠是int.可是在全部狀況下,賦值的時候會轉變爲其目標類型 . 詳見 [2].

[4] Python的字符串在解碼時候會以unicode來描述,可是一樣的能夠給其賦值爲ascii字符串  (此乃弦外之音).

 

Optional 字段與其默認值

如上所述,在描述一個消息的時候能夠用optional指定字段約束,一個消息能夠包含也能夠不包含optional元素。當一個消息被解析,若是其沒有一個optional字段,被解析的消息對象就會將其相對的字段設置爲其字段的默認值。這個默認值能夠在描述消息的時候被指定。例如。好比你想設置SearchRequest的 result_per_page的默認值爲10.

[cpp]  view plain copy print ?
 
  1. optional int32 result_per_page = 3 [default = 10];  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. optional int32 result_per_page = 3 [default = 10];  

 若是一個optional字段沒有被指定其默認值。其默認值被自動替換爲:

1.字符串:爲空字符串.

2.bool:爲false.

3.數字類型:爲0;

4.枚舉值:爲第一個枚舉值

枚舉值

當你定義消息格式的時候, 也許你但願其中的一個字段的的值爲一個預約義的值類表中的一個. 比方說, 在SearchRequest消息中你想定義一個 corpus 字段, corpus字段的值能夠爲:" UNIVERSALWEBIMAGESLOCALNEWSPRODUCTS 或者 VIDEO". 你能夠很是簡單的給你的消息添加一個枚舉類型 - 一個枚舉字段類型其值指定被指定爲一個常量的集合 (若是你嘗試賦值一個不同的值, 解析器將會認爲這個字段爲未知字段). 在下面的例子中 咱們給corpus字段指定爲枚舉類型與其可能的值 :

[cpp]  view plain copy print ?
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3 [default = 10];  
  5.   enum Corpus {  
  6.     UNIVERSAL = 0;  
  7.     WEB = 1;  
  8.     IMAGES = 2;  
  9.     LOCAL = 3;  
  10.     NEWS = 4;  
  11.     PRODUCTS = 5;  
  12.     VIDEO = 6;  
  13.   }  
  14.   optional Corpus corpus = 4 [default = UNIVERSAL];  
  15. }  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. message SearchRequest {  
  2.   required string query = 1;  
  3.   optional int32 page_number = 2;  
  4.   optional int32 result_per_page = 3 [default = 10];  
  5.   enum Corpus {  
  6.     UNIVERSAL = 0;  
  7.     WEB = 1;  
  8.     IMAGES = 2;  
  9.     LOCAL = 3;  
  10.     NEWS = 4;  
  11.     PRODUCTS = 5;  
  12.     VIDEO = 6;  
  13.   }  
  14.   optional Corpus corpus = 4 [default = UNIVERSAL];  
  15. }  

你能夠爲一個枚舉常量定義別名,若是你須要這樣作的話須要將allow_alias設置爲true。不然若是出現別名的話編譯器將會報錯! 

[cpp]  view plain copy print ?
 
  1. enum EnumAllowingAlias {  
  2.   option allow_alias = true;  
  3.   UNKNOWN = 0;  
  4.   STARTED = 1;  
  5.   RUNNING = 1;  
  6. }  
  7. enum EnumNotAllowingAlias {  
  8.   UNKNOWN = 0;  
  9.   STARTED = 1;  
  10.   // RUNNING = 1;  //不註釋這行的話會引起一個錯誤異常  
[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
 
  1. enum EnumAllowingAlias {  
  2.   option allow_alias = true;  
  3.   UNKNOWN = 0;  
  4.   STARTED = 1;  
  5.   RUNNING = 1;  
  6. }  
  7. enum EnumNotAllowingAlias {  
  8.   UNKNOWN = 0;  
  9.   STARTED = 1;  
  10.   // RUNNING = 1;  //不註釋這行的話會引起一個錯誤異常  

枚舉值的範圍必須在32位整數以內.枚舉值的編碼使用可變長度的整數,負數會很是低效因此,不推薦使用。你能夠在一個消息內部定義一個枚舉類型,好比上面的例子。或者也能夠在消息的外部定義。這些枚舉類型是能夠在.proto文件內中重用的,你能夠在消息內定義個枚舉類型。而後在不一樣的消息類型中使用它!可使用 MessageType.EnumType來訪問。當你運行編譯器編譯.proto文件中的枚舉類型時,生成的代碼會有一個相對應的枚舉值(JAVA 或者C++),或者有一個特別的EnumDescriptor類(python)用於在運行時生成一個符號常量集合。

更多關於枚舉類型的信息查詢 generated code guide 選擇你使用的語言。

 待續....................

相關文章
相關標籤/搜索