protocol buffer 使用之 .proto 定义规则
生活随笔
收集整理的這篇文章主要介紹了
protocol buffer 使用之 .proto 定义规则
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
message為主要關鍵字,類似于java中的class。
定義簡單的message類型:
SearchRequest.proto定義了每個查詢請求的消息格式,每個請求都會有查詢關鍵詞query,查詢結果的頁數,每頁的結果數量這三個屬性。于是
message?SearchRequest{
????????required?string?query?=?1;
????????optional?int32?page_number?=?2;
????????optional?int32?result_per_page?=3;
????????repeated?int32?samples?=?4?[packed= true];????
} message定義了三個field,每個field由名字和類型來組成。
????????required?string?query?=?1 ;
????????optional?int32?page_number?=?2 ;
????????optional?int32?result_per_page?=3 ;
????????repeated?int32?samples?=?4? [ packed=true ] ; ????
} 由于歷史原因,repeated字段如果是基本數字類型的話,不能有效地編碼。現在代碼可以使用特殊選項[packed=true]來得到更有效率的編碼。 注: ?由于required是永遠的,應該非常慎重地給message某個字段設置為required。如果未來你希望停止寫入或者輸出某個required字段,那就會成為問題;因為舊的reader將以為沒有這個字段無法初始化message,會丟掉這部分信息。一些來自google的工程師們指出使用required弊大于利,盡量使用optional和repeated。 這個觀點并不是通用的。
????????required?string?query?=?1 ;
????????optional?int32?page_number?=?2 ;
????????optional?int32?result_per_page?=3 ;
????????repeated?int32?samples?=?4? [ packed=true ] ; ????
}
????????required? string?query?=?1;?? //
????????optional?int32?page_number?=?2;?? // ?which?page?number?do?we?want?
????????optional?int32?result_per_page?=3;? // ?Number?of?results?to?return?per?page?
????????repeated?int32?samples?=?4?[packed= true];????
}
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?=?WEB];
}
??repeated?Result?result?=?1;
}
message?Result?{
??required? string?url?=?1;
??optional? string?title?=?2;
??repeated? string?snippets?=?3;
}
??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;
????}
??}
}
??repeated?group?Result?=?1?{
????required?string?url?=?2;
????optional?string?title?=?3;
????repeated?string?snippets?=?4;
??}
}
Extentions extensions 聲明一個消息中的一定范圍的field的順序數字用于進行擴展。其它人可以在自己的.proto文件中重新定義這些消息field,而不需要去修改原始的.proto文件 message?Foo?{
?? // ?
??extensions?100?to?199;
} 這些說明100-199的field是保留的。其它用戶可以用這些field在他們自己的.proto文件中添加新的fields給Foo。舉例: extend?Foo?{
??optional?int32?bar?=?126;
} 說明 Foo有一個optional的int32類型的名稱為bar的field ? 當Foo的message編碼后,數據格式就跟用戶在Foo中定義一個新的field完全一樣。但是你在程序中訪問extension field的方式與訪問正常的屬性略微有點不同。生成的extensions的訪問代碼是不同的。舉例:c++中如何set屬性bar的值:
Foo?foo;
foo.SetExtension(bar,15); 同樣,Foo 定義了模板訪問器 ?HasExtendsion(),ClearExtension(),GetExtension(),MutableExtension(),AddExtension(). 所有 訪問 ? ? ??
注: ?extensions能使用任何field類型,包括自定義消息類型。
message?Baz?{
??extend?Foo?{
????optional?int32?bar?=?126 ;
??}
??
} 在這個例子中, the C++ 代碼訪問訪問這個屬性:
Foo?foo;
foo.SetExtension(Baz::bar,?15); 換句話說,這么做唯一影響是bar定義在了Baz的范圍之內。 注意: 容易混淆的地方 聲明一個消息內部的繼承類并不意味著外部類和extended類有任何關系。 特別 以上的例子并不意味著Baz是任何Foo的子類。這些只是意味著符號bar是聲明在Baz的范圍之內的, 它看起來更像是個靜態成員。 一個通用的模式是在extensions的field范圍內來定義extensions,舉例說明,這里有一個Foo的extension作為Baz的一部分的屬性類型是Baz
message?Baz?{
??extend?Foo?{
????optional?Baz?foo_ext?=?127 ;
??}
??
} 沒有必要非得在message內部定義一個extension的類型。你也可以這么做:
message?Baz?{
??
}
//?This?can?even?be?in?a?different?file.
extend?Foo?{
??optional?Baz?foo_baz_ext?=?127 ;
} 事實上,上面的這個語法更加有效地避免混淆。正如上文所述,內部的那種語法語法對于不是熟悉extensions的人來說,經常會錯認為子類。
??extensions?1000?to?max ;
}
max?is?229?-?1,?or?536,870,911. 19000-19999是protocol buffers的使用的字段,所以這個范圍內的數字需要區別開來。 Packages 可以給一個.protol文件增加一個optional的package描述,來保證message盡量不會出現名字相同的重名。 package?foo.bar ;
message?Open?{?
?
} 也可以在指定field類型的時候使用 message?Foo?{
??
??required?foo.bar.Open?open?=?1 ;
??
} package會根據選擇的語言來生成不同的代碼: C++ ? ? ?生成的classes是用C++的namespace來區分的。舉例:Open?would?be?in?the?namespace?foo::bar。
Java ? ? ?package用于Java的package,除非你單獨的指定一個option?java_package?在.proto文件中。
Python ? package是被忽略的,因為Python的modules是通過它們的文件位置來組織的。
option?optimize_for? = ?CODE_SIZE ; cc_generic_services, java_generic_services, py_generic_services (file options) 無論如何,protoc編譯器會生成基于C++,Java,Python的抽象service代碼,這些默認都是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,消息使用不同的二進制格式來兼容谷歌內部使用的稱為MessageSet的舊格式。用戶在google以外使用,將不再需要使用這個option。 消息必須按照以下聲明
message?Foo?{
??option?message_set_wire_format?=?true ;
??extensions?4?to?max ;
} packed (field option) 如果設置為true, 一個repeated的基本integer類型的field,會使用一種更加緊湊的壓縮編碼。請注意,在2.3.0版之前,protocol生成的解析邏輯收到未預期的壓縮的數據將會忽略掉。因此,改變一個已經存在的field,一定會破壞其線性兼容性。在2.3.0以后,這種改變就是安全的,解析邏輯可以識別壓縮和不壓縮的格式,但是,一定要小心那些使用原先舊版本的protocol的程序。 repeated?int32?samples?=?4? [ packed=true ] ; deprecated (field option): 如果設置為true,表示這個field被廢棄,應該使用新代碼。大多數語言中,這個沒有任何影響。在java中,會生成@Deprecated的注釋。未來,其它語言代碼在field的訪問方法上也會生成相應的注釋。 optional?int32?old_field?=?6? [ deprecated=true ] ;
extend?google.protobuf.MessageOptions?{
??optional?string?my_option?=?51234 ;
}
message?MyMessage?{
??option?(my_option)?=?"Hello?world!" ;
} 這里我們定義了一個message級別的消息選項,當使用這個options的時候,選項的名稱必須用括號括起來,以表明它是一個extension。 我們在C++中讀取my_option的值就像下面這樣: string?value?=?MyMessage::descriptor()->options().GetExtension(my_option); 這里,MyMessage::descriptor()->options()返回的MessageOptions protocol類型 message。 讀取自定義就如同讀取繼承屬性一樣。 在Java中 String?value?=?MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption); 自定義options可以對任何message的組成元素進行定義 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" ;
??}
} 如果想使用在package里面的自定義的option,必須要option前使用包名,如下 //?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!" ;
} 最后一件事:既然自定義的options是extensions,他們必須指定field number就像其它field或者extension一樣。如果你要在公共應用中使用自定義的options,那么一定要確認你的field numbers是全局唯一的 你能通過多選項帶有一個extension 把它們放入一個子message中 message?FooOptions?{
??optional?int32?opt1?=?1 ;
??optional?string?opt2?=?2 ;
}
extend?google.protobuf.FieldOptions?{
??optional?FooOptions?foo_options?=?1234 ;
}
//?usage:
message?Bar?{
??optional?int32?a?=?1? [ (foo_options.opt1)?=?123,?(foo_options.opt2)?=?"baz" ] ;
? ?//?alternative?aggregate?syntax?(uses?TextFormat):
??optional?int32?b?=?2? [ (foo_options)?=?{?opt1:?123?opt2:?"baz"?} ] ;
} 生成class代碼 為了生成java、python、C++代碼,你需要運行protoc編譯器 protoc 編譯.proto文件。 編譯器運行命令: 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就是--proto_path的縮寫 輸出目錄 --cpp_out ? ? ? 生成C++代碼在DST_DIR目錄 --java_out ? ? ?生成Java代碼在DST_DIR目錄 --python_out ? ?生成Python代碼在DST_DIR目錄 有個額外的好處,如果DST是.zip或者.jar結尾,那么編譯器將會按照給定名字輸入到一個zip壓縮格式的文件中。 輸出到.jar會有一個jar指定的manifest文件。注意? 如果輸出文件已經存在,它將會被覆蓋;編譯器的智能不足以自動添加文件到一個存在的壓縮文件中。 你必須提供一個或者多個.proto文件用作輸入。雖然文件命名關聯到當前路徑,每個文件必須在import_path路徑中一邊編譯器能規定它的規范名稱
更新message 如果一個message 不再滿足所有需要,需要對字段進行調整.(舉例:對message增加一個額外的字段,但是仍然有支持舊格式message的代碼在運行) 要注意以下幾點:
1、不要修改已經存在字段的數字順序標示 2、可以增加optional或者repeated的新字段。這么做以后,所有通過舊格式message序列化的數據都可以通過新代碼來生成對應的對象,正如他們不會丟失任何required元素。 你應該為這些元素添加合理的默認值,以便新代碼可以與舊代碼生成的消息交互。 新代碼創建的消息中舊代碼不存在的字段,在解析的時候,舊代碼會忽略掉新增的字段。 無論如何,未知的field不會被丟棄,如果message晚點序列化,為。 注意 未知field對于Python來說當前不可用。 3、非required字段都可以轉為extension ,反之亦然,只要type和number保持不變。 4、int32, uint32, int64, uint64, and bool 是全兼容的。這意味著你能改變一個field從這些類型中的一個改變為另一個,而不用考慮會打破向前、向后兼容性。 如果一個數字是通過網絡傳輸而來的相應類型轉換,你將會遇到type在C++中遇到的問題(e.g. if a 64-bit number is read as an int32, it will be truncated to 32 bits) ? ? ? 5、sint32 and sint64 彼此兼容,但是不能兼容其它integer類型. 6、string and bytes 在UTF-8編碼下是兼容的.? 7、如果bytes包含一個message的編碼,內嵌message與bytes兼容. 8、fixed32 兼容 sfixed32, ?fixed64 兼容 sfixed64. 9、optional 兼容 repeated. 用一個repeat字段的編碼結果作為輸入,認為這個字段是可選擇的客戶端會這樣處理,如果是原始類型的話,獲得最后的輸入作為相應的option值;如果是message 類型,合并所有輸入元素.? 10、更改默認值通常是OK的.要記得默認值并不會通過網絡發送,如果一個程序接受一個特定字段沒有設置值的消息,應用將會使用自己的版本協議定義的默認值,不會看見發送者的默認值.
????????required?string?query?=?1;
????????optional?int32?page_number?=?2;
????????optional?int32?result_per_page?=3;
????????repeated?int32?samples?=?4?[packed= true];????
} message定義了三個field,每個field由名字和類型來組成。
- 指定field類型
- 分配標簽
- 指定field規則
????????required?string?query?=?1 ;
????????optional?int32?page_number?=?2 ;
????????optional?int32?result_per_page?=3 ;
????????repeated?int32?samples?=?4? [ packed=true ] ; ????
} 由于歷史原因,repeated字段如果是基本數字類型的話,不能有效地編碼。現在代碼可以使用特殊選項[packed=true]來得到更有效率的編碼。 注: ?由于required是永遠的,應該非常慎重地給message某個字段設置為required。如果未來你希望停止寫入或者輸出某個required字段,那就會成為問題;因為舊的reader將以為沒有這個字段無法初始化message,會丟掉這部分信息。一些來自google的工程師們指出使用required弊大于利,盡量使用optional和repeated。 這個觀點并不是通用的。
- 更多message類型
????????required?string?query?=?1 ;
????????optional?int32?page_number?=?2 ;
????????optional?int32?result_per_page?=3 ;
????????repeated?int32?samples?=?4? [ packed=true ] ; ????
}
- 添加注釋
????????required? string?query?=?1;?? //
????????optional?int32?page_number?=?2;?? // ?which?page?number?do?we?want?
????????optional?int32?result_per_page?=3;? // ?Number?of?results?to?return?per?page?
????????repeated?int32?samples?=?4?[packed= true];????
}
- .proto文件自動生成代碼
- 基本屬性 ?
- optional字段和默認值
- Enumerations
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?=?WEB];
}
- 自定義消息類型
??repeated?Result?result?=?1;
}
message?Result?{
??required? string?url?=?1;
??optional? string?title?=?2;
??repeated? string?snippets?=?3;
}
- import 定義
- 內部類
??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;
????}
??}
}
- Groups?
??repeated?group?Result?=?1?{
????required?string?url?=?2;
????optional?string?title?=?3;
????repeated?string?snippets?=?4;
??}
}
Extentions extensions 聲明一個消息中的一定范圍的field的順序數字用于進行擴展。其它人可以在自己的.proto文件中重新定義這些消息field,而不需要去修改原始的.proto文件 message?Foo?{
?? // ?
??extensions?100?to?199;
} 這些說明100-199的field是保留的。其它用戶可以用這些field在他們自己的.proto文件中添加新的fields給Foo。舉例: extend?Foo?{
??optional?int32?bar?=?126;
} 說明 Foo有一個optional的int32類型的名稱為bar的field ? 當Foo的message編碼后,數據格式就跟用戶在Foo中定義一個新的field完全一樣。但是你在程序中訪問extension field的方式與訪問正常的屬性略微有點不同。生成的extensions的訪問代碼是不同的。舉例:c++中如何set屬性bar的值:
Foo?foo;
foo.SetExtension(bar,15); 同樣,Foo 定義了模板訪問器 ?HasExtendsion(),ClearExtension(),GetExtension(),MutableExtension(),AddExtension(). 所有 訪問 ? ? ??
注: ?extensions能使用任何field類型,包括自定義消息類型。
- 內嵌的extensions
message?Baz?{
??extend?Foo?{
????optional?int32?bar?=?126 ;
??}
??
} 在這個例子中, the C++ 代碼訪問訪問這個屬性:
Foo?foo;
foo.SetExtension(Baz::bar,?15); 換句話說,這么做唯一影響是bar定義在了Baz的范圍之內。 注意: 容易混淆的地方 聲明一個消息內部的繼承類并不意味著外部類和extended類有任何關系。 特別 以上的例子并不意味著Baz是任何Foo的子類。這些只是意味著符號bar是聲明在Baz的范圍之內的, 它看起來更像是個靜態成員。 一個通用的模式是在extensions的field范圍內來定義extensions,舉例說明,這里有一個Foo的extension作為Baz的一部分的屬性類型是Baz
message?Baz?{
??extend?Foo?{
????optional?Baz?foo_ext?=?127 ;
??}
??
} 沒有必要非得在message內部定義一個extension的類型。你也可以這么做:
message?Baz?{
??
}
//?This?can?even?be?in?a?different?file.
extend?Foo?{
??optional?Baz?foo_baz_ext?=?127 ;
} 事實上,上面的這個語法更加有效地避免混淆。正如上文所述,內部的那種語法語法對于不是熟悉extensions的人來說,經常會錯認為子類。
- 選擇Extension 順序數字
??extensions?1000?to?max ;
}
max?is?229?-?1,?or?536,870,911. 19000-19999是protocol buffers的使用的字段,所以這個范圍內的數字需要區別開來。 Packages 可以給一個.protol文件增加一個optional的package描述,來保證message盡量不會出現名字相同的重名。 package?foo.bar ;
message?Open?{?
?
} 也可以在指定field類型的時候使用 message?Foo?{
??
??required?foo.bar.Open?open?=?1 ;
??
} package會根據選擇的語言來生成不同的代碼: C++ ? ? ?生成的classes是用C++的namespace來區分的。舉例:Open?would?be?in?the?namespace?foo::bar。
Java ? ? ?package用于Java的package,除非你單獨的指定一個option?java_package?在.proto文件中。
Python ? package是被忽略的,因為Python的modules是通過它們的文件位置來組織的。
- Packages和name?
option?optimize_for? = ?CODE_SIZE ; cc_generic_services, java_generic_services, py_generic_services (file options) 無論如何,protoc編譯器會生成基于C++,Java,Python的抽象service代碼,這些默認都是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,消息使用不同的二進制格式來兼容谷歌內部使用的稱為MessageSet的舊格式。用戶在google以外使用,將不再需要使用這個option。 消息必須按照以下聲明
message?Foo?{
??option?message_set_wire_format?=?true ;
??extensions?4?to?max ;
} packed (field option) 如果設置為true, 一個repeated的基本integer類型的field,會使用一種更加緊湊的壓縮編碼。請注意,在2.3.0版之前,protocol生成的解析邏輯收到未預期的壓縮的數據將會忽略掉。因此,改變一個已經存在的field,一定會破壞其線性兼容性。在2.3.0以后,這種改變就是安全的,解析邏輯可以識別壓縮和不壓縮的格式,但是,一定要小心那些使用原先舊版本的protocol的程序。 repeated?int32?samples?=?4? [ packed=true ] ; deprecated (field option): 如果設置為true,表示這個field被廢棄,應該使用新代碼。大多數語言中,這個沒有任何影響。在java中,會生成@Deprecated的注釋。未來,其它語言代碼在field的訪問方法上也會生成相應的注釋。 optional?int32?old_field?=?6? [ deprecated=true ] ;
- 自定義options
extend?google.protobuf.MessageOptions?{
??optional?string?my_option?=?51234 ;
}
message?MyMessage?{
??option?(my_option)?=?"Hello?world!" ;
} 這里我們定義了一個message級別的消息選項,當使用這個options的時候,選項的名稱必須用括號括起來,以表明它是一個extension。 我們在C++中讀取my_option的值就像下面這樣: string?value?=?MyMessage::descriptor()->options().GetExtension(my_option); 這里,MyMessage::descriptor()->options()返回的MessageOptions protocol類型 message。 讀取自定義就如同讀取繼承屬性一樣。 在Java中 String?value?=?MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption); 自定義options可以對任何message的組成元素進行定義 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" ;
??}
} 如果想使用在package里面的自定義的option,必須要option前使用包名,如下 //?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!" ;
} 最后一件事:既然自定義的options是extensions,他們必須指定field number就像其它field或者extension一樣。如果你要在公共應用中使用自定義的options,那么一定要確認你的field numbers是全局唯一的 你能通過多選項帶有一個extension 把它們放入一個子message中 message?FooOptions?{
??optional?int32?opt1?=?1 ;
??optional?string?opt2?=?2 ;
}
extend?google.protobuf.FieldOptions?{
??optional?FooOptions?foo_options?=?1234 ;
}
//?usage:
message?Bar?{
??optional?int32?a?=?1? [ (foo_options.opt1)?=?123,?(foo_options.opt2)?=?"baz" ] ;
? ?//?alternative?aggregate?syntax?(uses?TextFormat):
??optional?int32?b?=?2? [ (foo_options)?=?{?opt1:?123?opt2:?"baz"?} ] ;
} 生成class代碼 為了生成java、python、C++代碼,你需要運行protoc編譯器 protoc 編譯.proto文件。 編譯器運行命令: 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就是--proto_path的縮寫 輸出目錄 --cpp_out ? ? ? 生成C++代碼在DST_DIR目錄 --java_out ? ? ?生成Java代碼在DST_DIR目錄 --python_out ? ?生成Python代碼在DST_DIR目錄 有個額外的好處,如果DST是.zip或者.jar結尾,那么編譯器將會按照給定名字輸入到一個zip壓縮格式的文件中。 輸出到.jar會有一個jar指定的manifest文件。注意? 如果輸出文件已經存在,它將會被覆蓋;編譯器的智能不足以自動添加文件到一個存在的壓縮文件中。 你必須提供一個或者多個.proto文件用作輸入。雖然文件命名關聯到當前路徑,每個文件必須在import_path路徑中一邊編譯器能規定它的規范名稱
更新message 如果一個message 不再滿足所有需要,需要對字段進行調整.(舉例:對message增加一個額外的字段,但是仍然有支持舊格式message的代碼在運行) 要注意以下幾點:
1、不要修改已經存在字段的數字順序標示 2、可以增加optional或者repeated的新字段。這么做以后,所有通過舊格式message序列化的數據都可以通過新代碼來生成對應的對象,正如他們不會丟失任何required元素。 你應該為這些元素添加合理的默認值,以便新代碼可以與舊代碼生成的消息交互。 新代碼創建的消息中舊代碼不存在的字段,在解析的時候,舊代碼會忽略掉新增的字段。 無論如何,未知的field不會被丟棄,如果message晚點序列化,為。 注意 未知field對于Python來說當前不可用。 3、非required字段都可以轉為extension ,反之亦然,只要type和number保持不變。 4、int32, uint32, int64, uint64, and bool 是全兼容的。這意味著你能改變一個field從這些類型中的一個改變為另一個,而不用考慮會打破向前、向后兼容性。 如果一個數字是通過網絡傳輸而來的相應類型轉換,你將會遇到type在C++中遇到的問題(e.g. if a 64-bit number is read as an int32, it will be truncated to 32 bits) ? ? ? 5、sint32 and sint64 彼此兼容,但是不能兼容其它integer類型. 6、string and bytes 在UTF-8編碼下是兼容的.? 7、如果bytes包含一個message的編碼,內嵌message與bytes兼容. 8、fixed32 兼容 sfixed32, ?fixed64 兼容 sfixed64. 9、optional 兼容 repeated. 用一個repeat字段的編碼結果作為輸入,認為這個字段是可選擇的客戶端會這樣處理,如果是原始類型的話,獲得最后的輸入作為相應的option值;如果是message 類型,合并所有輸入元素.? 10、更改默認值通常是OK的.要記得默認值并不會通過網絡發送,如果一個程序接受一個特定字段沒有設置值的消息,應用將會使用自己的版本協議定義的默認值,不會看見發送者的默認值.
總結
以上是生活随笔為你收集整理的protocol buffer 使用之 .proto 定义规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 篮球30s倒计时
- 下一篇: Python程序设计与科学计算精录总结E