Protobuf3語言指南

Protobuf3語言指南

1. 什麼是protocol buffer

   protocolBuffer是用於結構化數據串行化的靈活、高效、自動的方法,有如XML,不過它更小、更快、也更簡單。你可以定義自己的數據結構,然後使用代碼生成器生成的代碼來讀寫這個數據結構。你甚至可以在無需重新部署程序的情況下更新數據結構。

2. 爲什麼要用protocol buffer

   在實際開發中經常會遇到的系統場景。比如:我們的客戶端程序是使用Java開發的,可能運行自不同的平臺,如:Linux、Windows或者是Android,而我們的服務器程序通常是基於Linux平臺並使用C++開發完成的。在這兩種程序之間進行數據通訊時存在多種方式用於設計消息格式,如:
   1. 直接傳遞C/C++語言中一字節對齊的結構體數據,只要結構體的聲明爲定長格式,那麼該方式對於C/C++程序而言就非常方便了,僅需將接收到的數據按照結構體類型強行轉換即可。事實上對於變長結構體也不會非常麻煩。在發送數據時,也只需定義一個結構體變量並設置各個成員變量的值之後,再以char*的方式將該二進制數據發送到遠端。反之,該方式對於Java開發者而言就會非常繁瑣,首先需要將接收到的數據存於ByteBuffer之中,再根據約定的字節序逐個讀取每個字段,並將讀取後的值再賦值給另外一個值對象中的域變量,以便於程序中其他代碼邏輯的編寫。對於該類型程序而言,聯調的基準是必須客戶端和服務器雙方均完成了消息報文構建程序的編寫後才能展開,而該設計方式將會直接導致Java程序開發的進度過慢。即便是Debug階段,也會經常遇到Java程序中出現各種域字段拼接的小錯誤。
   2. 使用SOAP協議(WebService)作爲消息報文的格式載體,由該方式生成的報文是基於文本格式的,同時還存在大量的XML描述信息,因此將會大大增加網絡IO的負擔。又由於XML解析的複雜性,這也會大幅降低報文解析的性能。總之,使用該設計方式將會使系統的整體運行性能明顯下降。
   3. 對於以上兩種方式所產生的問題,Protocol Buffer均可以很好的解決,不僅如此,Protocol Buffer還有一個非常重要的優點就是可以保證同一消息報文新舊版本之間的兼容性。 

3. 如何使用protocol buffer

定義一個消息類型

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

syntax = "proto3";
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 文件的第一行指定了你正在使用proto3語法:如果你沒有指定這個,編譯器會使用proto2。這個指定語法行必須是文件的非空非註釋的第一個行。
  • SearchRequest消息格式有3個字段,在消息中承載的數據分別對應於每一個字段。其中每個字段都有一個名字和一種類型。

指定字段類型

在上面的例子中,所有字段都是標量類型:一個string類型(query),兩個int型(page_number和result_per_page)。當然,你也可以爲字段指定其他的合成類型,包括枚舉(enumerations)或其他消息類型。

分配標識號

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

最小的標識號可以從1開始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (從FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的標識號, Protobuf協議實現中對這些進行了預留。如果非要在.proto文件中使用這些預留標識號,編譯時就會報警。同樣你也不能使用早期保留的標識號。

添加更多消息類型

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

message SearchRequest {
  string query = 1;  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

添加註釋

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

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

保留標識符(Reserved)

如果你通過刪除或者註釋所有域,以後的用戶可以重用標識號當你重新更新類型的時候。如果你使用舊版本加載相同的.proto文件這會導致嚴重的問題,包括數據損壞、隱私錯誤等等。現在有一種確保不會發生這種情況的方法就是指定保留標識符(and/or names, which can also cause issues for JSON serialization不明白什麼意思),protocol buffer的編譯器會警告未來嘗試使用這些域標識符的用戶。

message Foo {
  reserved 2, 15, 9 to 11;  reserved "foo", "bar";
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

注:不要在同一行reserved聲明中同時聲明域名字和標識號

從.proto文件生成了什麼?

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

  • 對C++來說,編譯器會爲每個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每一個消息有一個對應的類。
  • 對Java來說,編譯器爲每一個消息類型生成了一個.java文件,以及一個特殊的Builder類(該類是用來創建消息類接口的)。
  • 對Python來說,有點不太一樣——Python編譯器爲.proto文件中的每個消息類型生成一個含有靜態描述符的模塊,,該模塊與一個元類(metaclass)在運行時(runtime)被用來創建所需的Python數據訪問類。
  • 對go來說,編譯器會位每個消息類型生成了一個.pd.go文件。
  • 對於Ruby來說,編譯器會爲每個消息類型生成了一個.rb文件。
  • javaNano來說,編譯器輸出類似域java但是沒有Builder類
  • 對於Objective-C來說,編譯器會爲每個消息類型生成了一個pbobjc.h文件和pbobjcm文件,.proto文件中的每一個消息有一個對應的類。
  • 對於C#來說,編譯器會爲每個消息類型生成了一個.cs文件,.proto文件中的每一個消息有一個對應的類。

你可以從如下的文檔鏈接中獲取每種語言更多API(proto3版本的內容很快就公佈)。API Reference

標量數值類型

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


在java中,無符號32位和64位整型被表示成他們的整型對應形似,最高位被儲存在標誌位中。你可以在文章Protocol Buffer 編碼中,找到更多“序列化消息時各種類型如何編碼”的信息。

  1. 對於所有的情況,設定值會執行類型檢查以確保此值是有效。
  2. 64位或者無符號32位整型在解碼時被表示成爲ilong,但是在設置時可以使用int型值設定,在所有的情況下,值必須符合其設置其類型的要求。
  3. python中string被表示成在解碼時表示成unicode。但是一個ASCIIstring可以被表示成str類型。
  4. Integer在64位的機器上使用,string在32位機器上使用

默認值

當一個消息被解析的時候,如果被編碼的信息不包含一個特定的singular元素,被解析的對象鎖對應的域被設置位一個默認值,對於不同類型指定如下:

  • 對於strings,默認是一個空string
  • 對於bytes,默認是一個空的bytes
  • 對於bools,默認是false
  • 對於數值類型,默認是0
  • 對於枚舉,默認是第一個定義的枚舉值,必須爲0;
  • 對於消息類型(message),域沒有被設置,確切的消息是根據語言確定的,詳見generated code guide

    對於可重複域的默認值是空(通常情況下是對應語言中空列表)。

    注:對於標量消息域,一旦消息被解析,就無法判斷域釋放被設置爲默認值(例如,例如boolean值是否被設置爲false)還是根本沒有被設置。你應該在定義你的消息類型時非常注意。例如,比如你不應該定義boolean的默認值false作爲任何行爲的觸發方式。也應該注意如果一個標量消息域被設置爲標誌位,這個值不應該被序列化傳輸。

    查看generated code guide選擇你的語言的默認值的工作細節。

枚舉

當需要定義一個消息類型的時候,可能想爲一個字段指定某“預定義值序列”中的一個值。例如,假設要爲每一個SearchRequest消息添加一個 corpus字段,而corpus的值可能是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一個。 其實可以很容易地實現這一點:通過向消息定義中添加一個枚舉(enum)並且爲每個可能的值定義一個常量就可以了。

在下面的例子中,在消息格式中添加了一個叫做Corpus的枚舉類型——它含有所有可能的值 ——以及一個類型爲Corpus的字段:

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如你所見,Corpus枚舉的第一個常量映射爲0:每個枚舉類型必須將其第一個類型映射爲0。

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

在反序列化的過程中,無法識別的枚舉值會被保存在消息中,雖然這種表示方式需要依據所使用語言而定。在那些支持開放枚舉類型超出指定範圍之外的語言中(例如C++和Go),爲識別的值會被表示成所支持的整型。在使用封閉枚舉類型的語言中(Java),使用枚舉中的一個類型來表示未識別的值,並且可以使用所支持整型來訪問。在其他情況下,如果解析的消息被序列號,未識別的值將保持原樣。

關於如何在你的應用程序的消息中使用枚舉的更多信息,請查看所選擇的語言generated code guide

使用其他消息類型

你可以將其他消息類型用作字段類型。例如,假設在每一個SearchResponse消息中包含Result消息,此時可以在相同的.proto文件中定義一個Result消息類型,然後在SearchResponse消息中指定一個Result類型的字段,如:

message SearchResponse {  repeated Result results = 1;}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

導入定義

在上面的例子中,Result消息類型與SearchResponse是定義在同一文件中的。如果想要使用的消息類型已經在其他.proto文件中已經定義過了呢? 
你可以通過導入(importing)其他.proto文件中的定義來使用它們。要導入其他.proto文件的定義,你需要在你的文件中添加一個導入聲明,如:

import "myproject/other_protos.proto";
  • 1
  • 1

默認情況下你只能使用直接導入的.proto文件中的定義. 然而, 有時候你需要移動一個.proto文件到一個新的位置, 可以不直接移動.proto文件, 只需放入一個僞 .proto 文件在老的位置, 然後使用import public轉向新的位置。import public 依賴性會通過任意導入包含import public聲明的proto文件傳遞。例如:

// 這是新的proto
// All definitions are moved here
  • 1
  • 2
  • 1
  • 2
// 這是久的proto
// 這是所有客戶端正在導入的包
import public "new.proto";
import "other.proto";
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
// 客戶端proto
import "old.proto";
// 現在你可以使用新久兩種包的proto定義了。
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

通過在編譯器命令行參數中使用-I/--proto_pathprotocal 編譯器會在指定目錄搜索要導入的文件。如果沒有給出標誌,編譯器會搜索編譯命令被調用的目錄。通常你只要指定proto_path標誌爲你的工程根目錄就好。並且指定好導入的正確名稱就好。

使用proto2消息類型

在你的proto3消息中導入proto2的消息類型也是可以的,反之亦然,然後proto2枚舉不可以直接在proto3的標識符中使用(如果僅僅在proto2消息中使用是可以的)。

嵌套類型

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

message SearchResponse {  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

message SomeOtherMessage {
  SearchResponse.Result result = 1;}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

當然,你也可以將消息嵌套任意多層,如:

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;
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

更新一個消息類型

如果一個已有的消息格式已無法滿足新的需求——如,要在消息中添加一個額外的字段——但是同時舊版本寫的代碼仍然可用。不用擔心!更新消息而不破壞已有代碼是非常簡單的。在更新時只要記住以下的規則即可。

  • 不要更改任何已有的字段的數值標識。
  • 如果你增加新的字段,使用舊格式的字段仍然可以被你新產生的代碼所解析。你應該記住這些元素的默認值這樣你的新代碼就可以以適當的方式和舊代碼產生的數據交互。相似的,通過新代碼產生的消息也可以被舊代碼解析:只不過新的字段會被忽視掉。注意,未被識別的字段會在反序列化的過程中丟棄掉,所以如果消息再被傳遞給新的代碼,新的字段依然是不可用的(這和proto2中的行爲是不同的,在proto2中未定義的域依然會隨着消息被序列化)
  • 非required的字段可以移除——只要它們的標識號在新的消息類型中不再使用(更好的做法可能是重命名那個字段,例如在字段前添加“OBSOLETE_”前綴,那樣的話,使用的.proto文件的用戶將來就不會無意中重新使用了那些不該使用的標識號)。
  • int32, uint32, int64, uint64,和bool是全部兼容的,這意味着可以將這些類型中的一個轉換爲另外一個,而不會破壞向前、 向後的兼容性。如果解析出來的數字與對應的類型不相符,那麼結果就像在C++中對它進行了強制類型轉換一樣(例如,如果把一個64位數字當作int32來 讀取,那麼它就會被截斷爲32位的數字)。
  • sint32和sint64是互相兼容的,但是它們與其他整數類型不兼容。
  • string和bytes是兼容的——只要bytes是有效的UTF-8編碼。
  • 嵌套消息與bytes是兼容的——只要bytes包含該消息的一個編碼過的版本。
  • fixed32與sfixed32是兼容的,fixed64與sfixed64是兼容的。
  • 枚舉類型與int32,uint32,int64和uint64相兼容(注意如果值不相兼容則會被截斷),然而在客戶端反序列化之後他們可能會有不同的處理方式,例如,未識別的proto3枚舉類型會被保留在消息中,但是他的表示方式會依照語言而定。int類型的字段總會保留他們的
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

package foo.bar;
message Open { ... }
  • 1
  • 2
  • 1
  • 2

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

message Foo {
  ...
  required foo.bar.Open open = 1;
  ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

包的聲明符會根據使用語言的不同影響生成的代碼。

  • 對於C++,產生的類會被包裝在C++的命名空間中,如上例中的Open會被封裝在 foo::bar空間中; - 對於Java,包聲明符會變爲java的一個包,除非在.proto文件中提供了一個明確有java_package
  • 對於 Python,這個包聲明符是被忽略的,因爲Python模塊是按照其在文件系統中的位置進行組織的。
  • 對於Go,包可以被用做Go包名稱,除非你顯式的提供一個option go_package在你的.proto文件中。
  • 對於Ruby,生成的類可以被包裝在內置的Ruby名稱空間中,轉換成Ruby所需的大小寫樣式 (首字母大寫;如果第一個符號不是一個字母,則使用PB_前綴),例如Open會在Foo::Bar名稱空間中。
  • 對於javaNano包會使用Java包,除非你在你的文件中顯式的提供一個option java_package
  • 對於C#包可以轉換爲PascalCase後作爲名稱空間,除非你在你的文件中顯式的提供一個option csharp_namespace,例如,Open會在Foo.Bar名稱空間中

包及名稱的解析

Protocol buffer語言中類型名稱的解析與C++是一致的:首先從最內部開始查找,依次向外進行,每個包會被看作是其父類包的內部類。當然對於 (foo.bar.Baz)這樣以“.”分隔的意味着是從最外圍開始的。

ProtocolBuffer編譯器會解析.proto文件中定義的所有類型名。 對於不同語言的代碼生成器會知道如何來指向每個具體的類型,即使它們使用了不同的規則。

定義服務(Service)

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

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

最直觀的使用protocol buffer的RPC系統是gRPC一個由谷歌開發的語言和平臺中的開源的PRC系統,gRPC在使用protocl buffer時非常有效,如果使用特殊的protocol buffer插件可以直接爲您從.proto文件中產生相關的RPC代碼。

如果你不想使用gRPC,也可以使用protocol buffer用於自己的RPC實現,你可以從proto2語言指南中找到更多信息

還有一些第三方開發的PRC實現使用Protocol Buffer。參考第三方插件wiki查看這些實現的列表。


選項

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

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

如下就是一些常用的選擇:

  • java_package (文件選項) :這個選項表明生成java類所在的包。如果在.proto文件中沒有明確的聲明java_package,就採用默認的包名。當然了,默認方式產生的 java包名並不是最好的方式,按照應用名稱倒序方式進行排序的。如果不需要產生java代碼,則該選項將不起任何作用。如:
option java_package = "com.example.foo";
  • 1
  • 1
  • java_outer_classname (文件選項): 該選項表明想要生成Java類的名稱。如果在.proto文件中沒有明確的java_outer_classname定義,生成的class名稱將會根據.proto文件的名稱採用駝峯式的命名方式進行生成。如(foo_bar.proto生成的java類名爲FooBar.java),如果不生成java代碼,則該選項不起任何作用。如:
option java_outer_classname = "Ponycopter";
  • 1
  • 1
  • optimize_for(文件選項): 可以被設置爲 SPEED, CODE_SIZE,或者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;
  • 1
  • 1
  • cc_enable_arenas(文件選項):對於C++產生的代碼啓用arena allocation
  • objc_class_prefix(文件選項):設置Objective-C類的前綴,添加到所有Objective-C從此.proto文件產生的類和枚舉類型。沒有默認值,所使用的前綴應該是蘋果推薦的3-5個大寫字符,注意2個字節的前綴是蘋果所保留的。
  • deprecated(字段選項):如果設置爲true則表示該字段已經被廢棄,並且不應該在新的代碼中使用。在大多數語言中沒有實際的意義。在java中,這回變成@Deprecated註釋,在未來,其他語言的代碼生成器也許會在字標識符中產生廢棄註釋,廢棄註釋會在編譯器嘗試使用該字段時發出警告。如果字段沒有被使用你也不希望有新用戶使用它,嘗試使用保留語句替換字段聲明。
int32 old_field = 6 [deprecated=true];
  • 1
  • 1

生成訪問類

可以通過定義好的.proto文件來生成Java,Python,C++, Ruby, JavaNano, Objective-C,或者C# 代碼,需要基於.proto文件運行protocol buffer編譯器protoc。如果你沒有安裝編譯器,下載安裝包並遵照README安裝。對於Go,你還需要安裝一個特殊的代碼生成器插件。你可以通過GitHub上的protobuf庫找到安裝過程

通過如下方式調用protocol編譯器:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR 				--java_out=DST_DIR 				--python_out=DST_DIR 				--go_out=DST_DIR 				--ruby_out=DST_DIR 				--javanano_out=DST_DIR 				--objc_out=DST_DIR 				--csharp_out=DST_DIR path/to/file.proto
  • 1
  • 1
  • IMPORT_PATH聲明瞭一個.proto文件所在的解析import具體目錄。如果忽略該值,則使用當前目錄。如果有多個目錄則可以多次調用--proto_path,它們將會順序的被訪問並執行導入。-I=IMPORT_PATH--proto_path的簡化形式。
  • 當然也可以提供一個或多個輸出路徑: 
    • --cpp_out 在目標目錄DST_DIR中產生C++代碼,可以在C++代碼生成參考中查看更多。
    • --java_out 在目標目錄DST_DIR中產生Java代碼,可以在 Java代碼生成參考中查看更多。
    • --python_out 在目標目錄 DST_DIR 中產生Python代碼,可以在Python代碼生成參考中查看更多。
    • --go_out 在目標目錄 DST_DIR 中產生Go代碼,可以在GO代碼生成參考中查看更多。
    • --ruby_out在目標目錄 DST_DIR 中產生Go代碼,參考正在製作中。
    • --javanano_out在目標目錄DST_DIR中生成JavaNano,JavaNano代碼生成器有一系列的選項用於定製自定義生成器的輸出:你可以通過生成器的README查找更多信息,JavaNano參考正在製作中。
    • --objc_out在目標目錄DST_DIR中產生Object代碼,可以在Objective-C代碼生成參考中查看更多。
    • --csharp_out在目標目錄DST_DIR中產生Object代碼,可以在C#代碼生成參考中查看更多。
    • --php_out在目標目錄DST_DIR中產生Object代碼,可以在PHP代碼生成參考中查看更多。

作爲一個方便的拓展,如果DST_DIR以.zip或者.jar結尾,編譯器會將輸出寫到一個ZIP格式文件或者符合JAR標準的.jar文件中。注意如果輸出已經存在則會被覆蓋,編譯器還沒有智能到可以追加文件。 
- 你必須提議一個或多個.proto文件作爲輸入,多個.proto文件可以只指定一次。雖然文件路徑是相對於當前目錄的,每個文件必須位於其IMPORT_PATH下,以便每個文件可以確定其規範的名稱。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章