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