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] 來使其獲得有效的編碼.例如
注意:你應該當心將字段設置爲required,若是你但願在某些狀況下取消required字段的讀寫,它將改變字段爲optional屬性,舊的的讀取方將會認爲此消息不徹底。可能會無心的將其丟棄。你應該考慮自定義一個消息檢查程序。google的一些工程師認爲使用optinal字段的好處大於required。可是顯然這個觀點並非通用的。
多個消息類型能夠定義在同一個.proto文件內,這對定義多個有關聯的消息是是十分有用的。例如,若是你想定義一個用於回覆SearchResponse消息,你能夠像這樣在.proto內添加。
添加註釋
添加註釋的方式和C/C++是同樣的。使用//
當你使用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字段,被解析的消息對象就會將其相對的字段設置爲其字段的默認值。這個默認值能夠在描述消息的時候被指定。例如。好比你想設置SearchRequest的
result_per_page的默認值爲10.
若是一個optional字段沒有被指定其默認值。其默認值被自動替換爲:
1.字符串:爲空字符串.
2.bool:爲false.
3.數字類型:爲0;
4.枚舉值:爲第一個枚舉值
當你定義消息格式的時候, 也許你但願其中的一個字段的的值爲一個預約義的值類表中的一個. 比方說, 在SearchRequest消息中
你想定義一個 corpus
字段, corpus字段的值能夠爲:" UNIVERSAL
, WEB
, IMAGES
, LOCAL
, NEWS
, PRODUCTS
或者 VIDEO"
. 你能夠很是簡單的給你的消息添加一個枚舉類型 - 一個枚舉字段類型其值指定被指定爲一個常量的集合 (若是你嘗試賦值一個不同的值, 解析器將會認爲這個字段爲未知字段). 在下面的例子中 咱們給corpus字段指定爲枚舉類型與其可能的值 :
你能夠爲一個枚舉常量定義別名,若是你須要這樣作的話須要將allow_alias設置爲true。不然若是出現別名的話編譯器將會報錯!
枚舉值的範圍必須在32位整數以內.枚舉值的編碼使用可變長度的整數,負數會很是低效因此,不推薦使用。你能夠在一個消息內部定義一個枚舉類型,好比上面的例子。或者也能夠在消息的外部定義。這些枚舉類型是能夠在.proto文件內中重用的,你能夠在消息內定義個枚舉類型。而後在不一樣的消息類型中使用它!可使用 MessageType.EnumType來訪問。
當你運行編譯器編譯.proto文件中的枚舉類型時,生成的代碼會有一個相對應的枚舉值(JAVA 或者C++),或者有一個特別的EnumDescriptor類(python)用於在運行時生成一個符號常量集合。
更多關於枚舉類型的信息查詢 generated code guide 選擇你使用的語言。
待續....................