Protobuf語言指南,及使用

定義User.proto文件 html

package test.model;

option java_package = "test.model";
option java_outer_classname = "User";

message userInfo  {
  required int32 user_id = 1;      
  required string name = 2;
  required int32 age = 3;
  optional string phone = 4 [default = "123"];//默認值
}



運行命令生產java代碼
protoc.exe  --java_out=./ User.proto



google文檔:https://developers.google.com/protocol-buffers/docs/proto java

Protobuf語言指南 python

l  定義一個消息(message)類型 設計模式

l  標量值類型 api

l  Optional 的字段及默認值 安全

l  枚舉 服務器

l  使用其餘消息類型 框架

l  嵌套類型 ide

l  更新一個消息類型 函數

l  擴展

l  包(package)

l  定義服務(service)

l  選項(option)

l  生成訪問類

本指南描述了怎樣使用protocolbuffer語言來構造你的protocol buffer數據,包括.proto文件語法以及怎樣生成.proto文件的數據訪問類。

本文是一個參考指南——若是要查看如何使用本文中描述的多個特性的按部就班的例子,請在http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/tutorials.html中查找須要的語言的教程。

l  定義一個消息類型

先來看一個很是簡單的例子。假設你想定義一個「搜索請求」的消息格式,每個請求含有一個查詢字符串、你感興趣的查詢結果所在的頁數,以及每一頁多少條查詢結果。能夠採用以下的方式來定義消息類型的.proto文件了:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3;

}

SearchRequest消息格式有3個字段,在消息中承載的數據分別對應於每個字段。其中每一個字段都有一個名字和一種類型。

Ø  指定字段類型

在上面的例子中,全部字段都是標量類型:兩個整型(page_number和result_per_page),一個string類型(query)。固然,你也能夠爲字段指定其餘的合成類型,包括枚舉(enumerations)或其餘消息類型。

Ø   分配標識號

正如上述文件格式,在消息定義中,每一個字段都有惟一的一個標識符。這些標識符是用來在消息的二進制格式中識別各個字段的,一旦開始使用就不可以再改 變。注:[1,15]以內的標識號在編碼的時候會佔用一個字節。[16,2047]以內的標識號則佔用2個字節。因此應該爲那些頻繁出現的消息元素保留 [1,15]以內的標識號。切記:要爲未來有可能添加的、頻繁出現的標識號預留一些標識號。

最小的標識號能夠從1開始,最大到229 - 1, or 536,870,911。不可使用其中的[19000-19999]的標識號, Protobuf協議實現中對這些進行了預留。若是非要在.proto文件中使用這些預留標識號,編譯時就會報警。

Ø  指定字段規則

所指定的消息字段修飾符必須是以下之一:

²  required:一個格式良好的消息必定要含有1個這種字段。表示該值是必需要設置的;

²  optional:消息格式中該字段能夠有0個或1個值(不超過1個)。

²  repeated:在一個格式良好的消息中,這種字段能夠重複任意屢次(包括0次)。重複的值的順序會被保留。表示該值能夠重複,至關於java中的List。

因爲一些歷史緣由,基本數值類型的repeated的字段並無被儘量地高效編碼。在新的代碼中,用戶應該使用特殊選項[packed=true]來保證更高效的編碼。如:

repeated int32 samples = 4 [packed=true];

required是永久性的:在將一個字段標識爲required的時候,應該特別當心。若是在某些狀況下不想寫入或者發送一個required的 字段,將原始該字段修飾符更改成optional可能會遇到問題——舊版本的使用者會認爲不含該字段的消息是不完整的,從而可能會無目的的拒絕解析。在這 種狀況下,你應該考慮編寫特別針對於應用程序的、自定義的消息校驗函數。Google的一些工程師得出了一個結論:使用required弊多於利;他們更 願意使用optional和repeated而不是required。固然,這個觀點並不具備廣泛性。

Ø   添加更多消息類型

在一個.proto文件中能夠定義多個消息類型。在定義多個相關的消息的時候,這一點特別有用——例如,若是想定義與SearchResponse消息類型對應的回覆消息格式的話,你能夠將它添加到相同的.proto文件中,如:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3;

}

 

message SearchResponse {

 …

}

Ø  添加註釋

向.proto文件添加註釋,可使用C/C++/java風格的雙斜槓(//) 語法格式,如:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;// 最終返回的頁數

  optional int32 result_per_page = 3;// 每頁返回的結果數

}

Ø  從.proto文件生成了什麼?

當用protocolbuffer編譯器來運行.proto文件時,編譯器將生成所選擇語言的代碼,這些代碼能夠操做在.proto文件中定義的消息類型,包括獲取、設置字段值,將消息序列化到一個輸出流中,以及從一個輸入流中解析消息。

²  對C++來講,編譯器會爲每一個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每個消息有一個對應的類。

²  對Java來講,編譯器爲每個消息類型生成了一個.java文件,以及一個特殊的Builder類(該類是用來建立消息類接口的)。

²  對Python來講,有點不太同樣——Python編譯器爲.proto文件中的每一個消息類型生成一個含有靜態描述符的模塊,,該模塊與一個元類(metaclass)在運行時(runtime)被用來建立所需的Python數據訪問類。

你能夠從以下的文檔連接中獲取每種語言更多API。http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/overview.html

l  標量數值類型

一個標量消息字段能夠含有一個以下的類型——該表格展現了定義於.proto文件中的類型,以及與之對應的、在自動生成的訪問類中定義的類型:

.proto類型

Java 類型

C++類型

備註

double

double

double

 

float

float

float

 

int32

int

int32

使用可變長編碼方式。編碼負數時不夠高效——若是你的字段可能含有負數,那麼請使用sint32。

int64

long

int64

使用可變長編碼方式。編碼負數時不夠高效——若是你的字段可能含有負數,那麼請使用sint64。

sint32

int

int32

使用可變長編碼方式。有符號的整型值。編碼時比一般的int32高效。

sint64

long

int64

使用可變長編碼方式。有符號的整型值。編碼時比一般的int64高效。

fixed32

int

uint32

老是4個字節。若是數值老是比老是比228大的話,這個類型會比uint32高效。

fixed64

long

uint64

老是8個字節。若是數值老是比老是比256大的話,這個類型會比uint64高效。

sfixed32

int

int32

老是4個字節。

fixed64

long

int64

老是8個字節。

bool

boolean

bool

 

string

String

string

一個字符串必須是UTF-8編碼或者7-bit ASCII編碼的文本。

bytes

ByteString

string

可能包含任意順序的字節數據。

你能夠在文章http://code.google.com/apis/protocolbuffers/docs/encoding.html 中,找到更多「序列化消息時各類類型如何編碼」的信息。

l  Optional的字段和默認值

如上所述,消息描述中的一個元素能夠被標記爲「可選的」(optional)。一個格式良好的消息能夠包含0個或一個optional的元素。當解 析消息時,若是它不包含optional的元素值,那麼解析出來的對象中的對應字段就被置爲默認值。默認值能夠在消息描述文件中指定。例如,要爲 SearchRequest消息的result_per_page字段指定默認值10,在定義消息格式時以下所示:

optional int32 result_per_page = 3 [default = 10];

若是沒有爲optional的元素指定默認值,就會使用與特定類型相關的默認值:對string來講,默認值是空字符串。對bool來講,默認值是false。對數值類型來講,默認值是0。對枚舉來講,默認值是枚舉類型定義中的第一個值。

枚舉

當須要定義一個消息類型的時候,可能想爲一個字段指定某「預約義值序列」中的一個值。例如,假設要爲每個SearchRequest消息添加一個 corpus字段,而corpus的值多是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一個。 其實能夠很容易地實現這一點:經過向消息定義中添加一個枚舉(enum)就能夠了。一個enum類型的字段只能用指定的常量集中的一個值做爲其值(若是嘗 試指定不一樣的值,解析器就會把它看成一個未知的字段來對待)。在下面的例子中,在消息格式中添加了一個叫作Corpus的枚舉類型——它含有全部可能的值 ——以及一個類型爲Corpus的字段:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3 [default = 10];

  enum Corpus {

    UNIVERSAL = 0;

    WEB = 1;

    IMAGES = 2;

    LOCAL = 3;

    NEWS = 4;

    PRODUCTS = 5;

    VIDEO = 6;

  }

  optional Corpus corpus = 4 [default = UNIVERSAL];

}

枚舉常量必須在32位整型值的範圍內。由於enum值是使用可變編碼方式的,對負數不夠高效,所以不推薦在enum中使用負數。如上例所示,能夠在 一個消息定義的內部或外部定義枚舉——這些枚舉能夠在.proto文件中的任何消息定義裏重用。固然也能夠在一個消息中聲明一個枚舉類型,而在另外一個不一樣 的消息中使用它——採用MessageType.EnumType的語法格式。

當對一個使用了枚舉的.proto文件運行protocol buffer編譯器的時候,生成的代碼中將有一個對應的enum(對Java或C++來講),或者一個特殊的EnumDescriptor類(對 Python來講),它被用來在運行時生成的類中建立一系列的整型值符號常量(symbolic constants)。

關於如何在你的應用程序的消息中使用枚舉的更多信息,請查看所選擇的語言http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/overview.html。

l  使用其餘消息類型

你能夠將其餘消息類型用做字段類型。例如,假設在每個SearchResponse消息中包含Result消息,此時能夠在相同的.proto文件中定義一個Result消息類型,而後在SearchResponse消息中指定一個Result類型的字段,如:

message SearchResponse {

  repeated Result result = 1;

}

message Result {

  required string url = 1;

  optional string title = 2;

  repeated string snippets = 3;

}

Ø  導入定義

在上面的例子中,Result消息類型與SearchResponse是定義在同一文件中的。若是想要使用的消息類型已經在其餘.proto文件中已經定義過了呢?

 你能夠經過導入(importing)其餘.proto文件中的定義來使用它們。要導入其餘.proto文件的定義,你須要在你的文件中添加一個導入聲明,如:

import "myproject/other_protos.proto";

protocol編譯器就會在一系列目錄中查找須要被導入的文件,這些目錄經過protocol編譯器的命令行參數-I/–import_path指定。若是不提供參數,編譯器就在其調用目錄下查找。

l  嵌套類型

你能夠在其餘消息類型中定義、使用消息類型,在下面的例子中,Result消息就定義在SearchResponse消息內,如:

message SearchResponse {

  message Result {

    required string url = 1;

    optional string title = 2;

    repeated string snippets = 3;

  }

  repeated Result result = 1;

}

若是你想在它的父消息類型的外部重用這個消息類型,你須要以Parent.Type的形式使用它,如:

message SomeOtherMessage {

  optional SearchResponse.Result result = 1;

}

固然,你也能夠將消息嵌套任意多層,如:

message Outer {                  // Level 0

  message MiddleAA {  // Level 1

    message Inner {   // Level 2

      required int64 ival = 1;

      optional bool  booly = 2;

    }

  }

  message MiddleBB {  // Level 1

    message Inner {   // Level 2

      required int32 ival = 1;

      optional bool  booly = 2;

    }

  }

}

Ø  

注:該特性已被棄用,在建立新的消息類型的時候,不該該再使用它——可使用嵌套消息類型來代替它。

「組」是指在消息定義中嵌套信息的另外一種方法。好比,在SearchResponse中包含若干Result的另外一種方法是 :

message SearchResponse {

  repeated group Result = 1 {

    required string url = 2;

    optional string title = 3;

    repeated string snippets = 4;

  }

}

一個「組」只是簡單地將一個嵌套消息類型和一個字段捆綁到一個單獨的聲明中。在代碼中,能夠把它當作是含有一個Result類型、名叫result的字段的消息(後面的名字被轉換成了小寫,因此它不會與前面的衝突)。

所以,除了數據傳輸格式不一樣以外,這個例子與上面的SearchResponse例子是徹底等價的。

l  更新一個消息類型

若是一個已有的消息格式已沒法知足新的需求——如,要在消息中添加一個額外的字段——可是同時舊版本寫的代碼仍然可用。不用擔憂!更新消息而不破壞已有代碼是很是簡單的。在更新時只要記住如下的規則便可。

²  不要更改任何已有的字段的數值標識。

²  所添加的任何字段都必須是optional或repeated的。這就意味着任何使用「舊」的消息格式的代碼序列化的消息能夠被新的代碼所解析,由於它們 不會丟掉任何required的元素。應該爲這些元素設置合理的默認值,這樣新的代碼就可以正確地與老代碼生成的消息交互了。相似地,新的代碼建立的消息 也能被老的代碼解析:老的二進制程序在解析的時候只是簡單地將新字段忽略。然而,未知的字段是沒有被拋棄的。此後,若是消息被序列化,未知的字段會隨之一 起被序列化——因此,若是消息傳到了新代碼那裏,則新的字段仍然可用。注意:對Python來講,對未知字段的保留策略是無效的。

²  非required的字段能夠移除——只要它們的標識號在新的消息類型中再也不使用(更好的作法多是重命名那個字段,例如在字段前添加「OBSOLETE_」前綴,那樣的話,使用的.proto文件的用戶未來就不會無心中從新使用了那些不應使用的標識號)。

²  一個非required的字段能夠轉換爲一個擴展,反之亦然——只要它的類型和標識號保持不變。

²  int32, uint32, int64, uint64,和bool是所有兼容的,這意味着能夠將這些類型中的一個轉換爲另一個,而不會破壞向前、 向後的兼容性。若是解析出來的數字與對應的類型不相符,那麼結果就像在C++中對它進行了強制類型轉換同樣(例如,若是把一個64位數字看成int32來 讀取,那麼它就會被截斷爲32位的數字)。

²  sint32和sint64是互相兼容的,可是它們與其餘整數類型不兼容。

²  string和bytes是兼容的——只要bytes是有效的UTF-8編碼。

²  嵌套消息與bytes是兼容的——只要bytes包含該消息的一個編碼過的版本。

²  fixed32與sfixed32是兼容的,fixed64與sfixed64是兼容的。

l  擴展

經過擴展,能夠將一個範圍內的字段標識號聲明爲可被第三方擴展所用。而後,其餘人就能夠在他們本身的.proto文件中爲該消息類型聲明新的字段,而沒必要去編輯原始文件了。看個具體例子:

message Foo {

  // …

  extensions 100 to 199;

}

這個例子代表:在消息Foo中,範圍[100,199]以內的字段標識號被保留爲擴展用。如今,其餘人就能夠在他們本身的.proto文件中添加新字段到Foo裏了,可是添加的字段標識號要在指定的範圍內——例如:

extend Foo {

  optional int32 bar = 126;

}

這個例子代表:消息Foo如今有一個名爲bar的optional int32字段。

當用戶的Foo消息被編碼的時候,數據的傳輸格式與用戶在Foo裏定義新字段的效果是徹底同樣的。

然而,要在程序代碼中訪問擴展字段的方法與訪問普通的字段稍有不一樣——生成的數據訪問代碼爲擴展準備了特殊的訪問函數來訪問它。例如,下面是如何在C++中設置bar的值:

Foo foo;
foo.SetExtension(bar, 15);

相似地,Foo類也定義了模板函數 HasExtension(),ClearExtension(),GetExtension(),MutableExtension(),以及 AddExtension()。這些函數的語義都與對應的普通字段的訪問函數相符。要查看更多使用擴展的信息,請參考相應語言的代碼生成指南。注:擴展可 以是任何字段類型,包括消息類型。

l  嵌套的擴展

能夠在另外一個類型的範圍內聲明擴展,如:

message Baz {

  extend Foo {

    optional int32 bar = 126;

  }

  …

}

在此例中,訪問此擴展的C++代碼以下:

Foo foo;

foo.SetExtension(Baz::bar, 15);

一個一般的設計模式就是:在擴展的字段類型的範圍內定義該擴展——例如,下面是一個Foo的擴展(該擴展是Baz類型的),其中,擴展被定義爲了Baz的一部分:

message Baz {

  extend Foo {

    optional Baz foo_ext = 127;

  }

  …

}

然而,並無強制要求一個消息類型的擴展必定要定義在那個消息中。也能夠這樣作:

message Baz {

  …

}

 

extend Foo {

  optional Baz foo_baz_ext = 127;

}

 

事實上,這種語法格式更能防止引發混淆。正如上面所提到的,嵌套的語法一般被錯誤地認爲有子類化的關係——尤爲是對那些還不熟悉擴展的用戶來講。

Ø  選擇可擴展的標符號

在同一個消息類型中必定要確保兩個用戶不會擴展新增相同的標識號,不然可能會致使數據的不一致。能夠經過爲新項目定義一個可擴展標識號規則來防止該狀況的發生。

若是標識號須要很大的數量時,能夠將該可擴展標符號的範圍擴大至max,其中max是229 - 1, 或536,870,911。以下所示:

message Foo {

  extensions 1000 to max;

}

一般狀況下在選擇標符號時,標識號產生的規則中應該避開[19000-19999]之間的數字,由於這些已經被Protocol Buffers實現中預留了。

l  包(Package)

固然能夠爲.proto文件新增一個可選的package聲明符,用來防止不一樣的消息類型有命名衝突。如:

package foo.bar;

message Open { ... }

在其餘的消息格式定義中可使用包名+消息名的方式來定義域的類型,如:

message Foo {

  ...

  required foo.bar.Open open = 1;

  ...

}

包的聲明符會根據使用語言的不一樣影響生成的代碼。對於C++,產生的類會被包裝在C++的命名空間中,如上例中的Open會被封裝在 foo::bar空間中;對於Java,包聲明符會變爲java的一個包,除非在.proto文件中提供了一個明確有java_package;對於 Python,這個包聲明符是被忽略的,由於Python模塊是按照其在文件系統中的位置進行組織的。

Ø  包及名稱的解析

Protocol buffer語言中類型名稱的解析與C++是一致的:首先從最內部開始查找,依次向外進行,每一個包會被看做是其父類包的內部類。固然對於 (foo.bar.Baz)這樣以「.」分隔的意味着是從最外圍開始的。ProtocolBuffer編譯器會解析.proto文件中定義的全部類型名。 對於不一樣語言的代碼生成器會知道如何來指向每一個具體的類型,即便它們使用了不一樣的規則。

l  定義服務(Service)

若是想要將消息類型用在RPC(遠程方法調用)系統中,能夠在.proto文件中定義一個RPC服務接口,protocol buffer編譯器將會根據所選擇的不一樣語言生成服務接口代碼及存根。如,想要定義一個RPC服務並具備一個方法,該方法可以接收 SearchRequest並返回一個SearchResponse,此時能夠在.proto文件中進行以下定義:

service SearchService {

  rpc Search (SearchRequest) returns (SearchResponse);

}

 

protocol編譯器將產生一個抽象接口SearchService以及一個相應的存根實現。存根將全部的調用指向RpcChannel,它是一 個抽象接口,必須在RPC系統中對該接口進行實現。如,能夠實現RpcChannel以完成序列化消息並經過HTTP方式來發送到一個服務器。換句話說, 產生的存根提供了一個類型安全的接口用來完成基於protocolbuffer的RPC調用,而不是將你限定在一個特定的RPC的實現中。C++中的代碼 以下所示:

using google::protobuf;

protobuf::RpcChannel* channel;
protobuf::RpcController* controller;
SearchService* service;
SearchRequest request;
SearchResponse response;

void DoSearch() {
  // You provide classes MyRpcChannel and MyRpcController, which implement
  // the abstract interfaces protobuf::RpcChannel and protobuf::RpcController.
  channel = new MyRpcChannel("somehost.example.com:1234");
  controller = new MyRpcController;
  

// The protocol compiler generates the SearchService class based on the
  // definition given above.
 

service = new SearchService::Stub(channel);
  // Set up the request.
  request.set_query("protocol buffers");

  // Execute the RPC.
  service->Search(controller, request, response, protobuf::NewCallback(&Done));
}

void Done() {
  delete service;
  delete channel;
  delete controller;
}

全部service類都必須實現Service接口,它提供了一種用來調用具體方法的方式,即在編譯期不須要知道方法名及它的輸入、輸出類型。在服務器端,經過服務註冊它能夠被用來實現一個RPC Server。

using google::protobuf;

class ExampleSearchService : public SearchService {
 public:
  void Search(protobuf::RpcController* controller,
              const SearchRequest* request,
              SearchResponse* response,
              protobuf::Closure* done) {
    if (request->query() == "google") {
      response->add_result()->set_url("http://www.google.com");
    } else if (request->query() == "protocol buffers") {
      response->add_result()->set_url("http://protobuf.googlecode.com");
    }
    done->Run();
  }
};

int main() {
  // You provide class MyRpcServer.  It does not have to implement any
  // particular interface; this is just an example.
  MyRpcServer server;

  protobuf::Service* service = new ExampleSearchService;
  server.ExportOnPort(1234, service);
  server.Run();

  delete service;
  return 0;
}

l  選項(Options)

在定義.proto文件時可以標註一系列的options。Options並不改變整個文件聲明的含義,但卻可以影響特定環境下處理方式。完整的可用選項能夠在google/protobuf/descriptor.proto找到。

一些選項是文件級別的,意味着它能夠做用於最外範圍,不包含在任何消息內部、enum或服務定義中。一些選項是消息級別的,意味着它能夠用在消息定 義的內部。固然有些選項能夠做用在域、enum類型、enum值、服務類型及服務方法中。到目前爲止,並無一種有效的選項能做用於全部的類型。

以下就是一些經常使用的選擇:

²  java_package (file option): 這個選項代表生成java類所在的包。若是在.proto文件中沒有明確的聲明java_package,就採用默認的包名。固然了,默認方式產生的 java包名並非最好的方式,按照應用名稱倒序方式進行排序的。若是不須要產生java代碼,則該選項將不起任何做用。如:

option java_package = "com.example.foo";

²  java_outer_classname (file option): 該選項代表想要生成Java類的名稱。若是在.proto文件中沒有明確的java_outer_classname定義,生成的class名稱將會根據.proto文件的名稱採用駝峯式的命名方式進行生成。如(foo_bar.proto生成的java類名爲FooBar.java),若是不生成java代碼,則該選項不起任何做用。如:

option java_outer_classname = "Ponycopter";

²  optimize_for (fileoption): 能夠被設置爲 SPEED, CODE_SIZE,or LITE_RUNTIME。這些值將經過以下的方式影響C++及java代碼的生成:

·        SPEED (default): protocol buffer編譯器將經過在消息類型上執行序列化、語法分析及其餘通用的操做。這種代碼是最優的。

·        CODE_SIZE: protocol buffer編譯器將會產生最少許的類,經過共享或基於反射的代碼來實現序列化、語法分析及各類其它操做。採用該方式產生的代碼將比SPEED要少得多, 可是操做要相對慢些。固然實現的類及其對外的API與SPEED模式都是同樣的。這種方式常常用在一些包含大量的.proto文件並且並不盲目追求速度的 應用中。

·        LITE_RUNTIME: protocol buffer編譯器依賴於運行時核心類庫來生成代碼(即採用libprotobuf-lite 替代libprotobuf)。這種核心類庫因爲忽略了一 些描述符及反射,要比全類庫小得多。這種模式常常在移動手機平臺應用多一些。編譯器採用該模式產生的方法實現與SPEED模式不相上下,產生的類經過實現 MessageLite接口,但它僅僅是Messager接口的一個子集。

option optimize_for = CODE_SIZE;

²  cc_generic_services, java_generic_services, py_generic_services (file options): 在C++、java、python中protocol buffer編譯器是否應該基於服務定義產生抽象服務代碼。因爲歷史遺留問題,該值默認是true。可是自2.3.0版本以來,它被認爲經過提供代碼生成 器插件來對RPC實現更可取,而不是依賴於「抽象」服務。

// This file relies on plugins to generate service code.

option cc_generic_services = false;

option java_generic_services = false;

option py_generic_services = false;

²  message_set_wire_format (message option):若是該值被設置爲true,該消息將使用一種不一樣的二進制格式來與Google內部的MessageSet的老格式相兼容。對於Google外部的用戶來講,該選項將不會被用到。以下所示:

message Foo {

  option message_set_wire_format = true;

  extensions 4 to max;

}

²  packed (field option): 若是該選項在一個整型基本類型上被設置爲真,則採用更緊湊的編碼方式。固然使用該值並不會對數值形成任何損失。在2.3.0版本以前,解析器將會忽略那些 非指望的包裝值。所以,它不可能在不破壞現有框架的兼容性上而改變壓縮格式。在2.3.0以後,這種改變將是安全的,解析器可以接受上述兩種格式,可是在 處理protobuf老版本程序時,仍是要多留意一下。

repeated int32 samples = 4 [packed=true];

²  deprecated (field option): 若是該選項被設置爲true,代表該字段已經被棄用了,在新代碼中不建議使用。在多數語言中,這並無實際的含義。在java中,它將會變成一個 @Deprecated註釋。也許在未來,其它基於語言聲明的代碼在生成時也會如此使用,當使用該字段時,編譯器將自動報警。如:

optional int32 old_field = 6 [deprecated=true];

Ø 自定義選項

ProtocolBuffers容許自定義並使用選項。該功能應該屬於一個高級特性,對於大部分人是用不到的。因爲options是定在 google/protobuf/descriptor.proto中的,所以你能夠在該文件中進行擴展,定義本身的選項。如:

import "google/protobuf/descriptor.proto";

 

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

 

message MyMessage {

  option (my_option) = "Hello world!";

}

在上述代碼中,經過對MessageOptions進行擴展定義了一個新的消息級別的選項。當使用該選項時,選項的名稱須要使用()包裹起來,以代表它是一個擴展。在C++代碼中能夠看出my_option是以以下方式被讀取的。

string value = MyMessage::descriptor()->options().GetExtension(my_option);

在Java代碼中的讀取方式以下:

String value = MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption);

正如上面的讀取方式,定製選項對於Python並不支持。定製選項在protocol buffer語言中可用於任何結構。下面就是一些具體的例子:

import "google/protobuf/descriptor.proto";

 

extend google.protobuf.FileOptions {

  optional string my_file_option = 50000;

}

extend google.protobuf.MessageOptions {

  optional int32 my_message_option = 50001;

}

extend google.protobuf.FieldOptions {

  optional float my_field_option = 50002;

}

extend google.protobuf.EnumOptions {

  optional bool my_enum_option = 50003;

}

extend google.protobuf.EnumValueOptions {

  optional uint32 my_enum_value_option = 50004;

}

extend google.protobuf.ServiceOptions {

  optional MyEnum my_service_option = 50005;

}

extend google.protobuf.MethodOptions {

  optional MyMessage my_method_option = 50006;

}

 

option (my_file_option) = "Hello world!";

 

message MyMessage {

  option (my_message_option) = 1234;

 

  optional int32 foo = 1 [(my_field_option) = 4.5];

  optional string bar = 2;

}

 

enum MyEnum {

  option (my_enum_option) = true;

 

  FOO = 1 [(my_enum_value_option) = 321];

  BAR = 2;

}

 

message RequestType {}

message ResponseType {}

 

service MyService {

  option (my_service_option) = FOO;

 

  rpc MyMethod(RequestType) returns(ResponseType) {

    // Note:  my_method_option has type MyMessage.  We can set each field

    //   within it using a separate "option" line.

    option (my_method_option).foo = 567;

    option (my_method_option).bar = "Some string";

  }

}

注:若是要在該選項定義以外使用一個自定義的選項,必需要由包名 + 選項名來定義該選項。如:

// foo.proto

import "google/protobuf/descriptor.proto";

package foo;

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

// bar.proto

import "foo.proto";

package bar;

message MyMessage {

  option (foo.my_option) = "Hello world!";

}

最後一件事情須要注意:由於自定義選項是可擴展的,它必須象其它的域或擴展同樣來定義標識號。正如上述示例,[50000-99999]已經被佔 用,該範圍內的值已經被內部所使用,固然了你能夠在內部應用中隨意使用。若是你想在一些公共應用中進行自定義選項,你必須確保它是全局惟一的。能夠經過protobuf-global-extension-registry@google.com來獲取全局惟一標識號。

l  生成訪問類

能夠經過定義好的.proto文件來生成Java、Python、C++代碼,須要基於.proto文件運行protocol buffer編譯器protoc。運行的命令以下所示:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

·        IMPORT_PATH聲明瞭一個.proto文件所在的具體目錄。若是忽略該值,則使用當前目錄。若是有多個目錄則能夠 對--proto_path 寫屢次,它們將會順序的被訪問並執行導入。-I=IMPORT_PATH是它的簡化形式。

·        固然也能夠提供一個或多個輸出路徑:

o   --cpp_out 在目標目錄DST_DIR中產生C++代碼,能夠在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /cpp-generated.html中查看更多。

o   --java_out 在目標目錄DST_DIR中產生Java代碼,能夠在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /java-generated.html中查看更多。

o   --python_out 在目標目錄 DST_DIR 中產生Python代碼,能夠在http://code.google.com/intl/zh-CN/apis/protocolbuffers /docs/reference/python-generated.html中查看更多。

     做爲一種額外的使得,若是DST_DIR 是以.zip或.jar結尾的,編譯器將輸出結果打包成一個zip格式的歸檔文件。.jar將會輸出一個 Java JAR聲明必須的manifest文件。注:若是該輸出歸檔文件已經存在,它將會被重寫,編譯器並無作到足夠的智能來爲已經存在的歸檔文件添加新的文 件。

·        你必須提供一個或多個.proto文件做爲輸入。多個.proto文件可以一次所有聲明。雖然這些文件是相對於當前目錄來命名的,每一個文件必須在一個IMPORT_PATH中,只有如此編譯器才能夠決定它的標準名稱。

相關文章
相關標籤/搜索