一、更新一個消息類型
如果一個已有的消息格式已無法滿足新的需求——如,要在消息中添加一個額外的字段——但是同時舊版本寫的代碼仍然可用。不用擔心!更新消息而不破壞已有代碼是非常簡單的。在更新時只要記住以下的規則即可。
1.不要更改任何已有的字段的數值標識。
2.所添加的任何字段都必須是optional或repeated的。這就意味着任何使用“舊”的消息格式的代碼序列化的消息可以被新的代碼所解析,因為它們不會丟掉任何required的元素。應該為這些元素設置合理的默認值,這樣新的代碼就能夠正確地與老代碼生成的消息交互了。類似地,新的代碼創建的消息也能被老的代碼解析:老的二進制程序在解析的時候只是簡單地將新字段忽略。然而,未知的字段是沒有被拋棄的。此后,如果消息被序列化,未知的字段會隨之一起被序列化——所以,如果消息傳到了新代碼那里,則新的字段仍然可用。注意:對Python來說,對未知字段的保留策略是無效的。
3.非required的字段可以移除——只要它們的標識號在新的消息類型中不再使用(更好的做法可能是重命名那個字段,例如在字段前添加“OBSOLETE_”前綴,那樣的話,使用的.proto文件的用戶將來就不會無意中重新使用了那些不該使用的標識號)。
4.一個非required的字段可以轉換為一個擴展,反之亦然——只要它的類型和標識號保持不變。
5.int32, uint32, int64, uint64,和bool是全部兼容的,這意味着可以將這些類型中的一個轉換為另外一個,而不會破壞向前、向后的兼容性。如果解析出來的數字與對應的類型不相符,那么結果就像在C++中對它進行了強制類型轉換一樣(例如,如果把一個64位數字當作int32來讀取,那么它就會被截斷為32位的數字)。
6.sint32和sint64是互相兼容的,但是它們與其他整數類型不兼容。
7.string和bytes是兼容的——只要bytes是有效的UTF-8編碼。
8.嵌套消息與bytes是兼容的——只要bytes包含該消息的一個編碼過的版本。
9.fixed32與sfixed32是兼容的,fixed64與sfixed64是兼容的。
二、擴展
通過擴展,可以將一個范圍內的字段標識號聲明為可被第三方擴展所用。然后,其他人就可以在他們自己的.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()。這些函數的語義都與對應的普通字段的訪問函數相符。要查看更多使用擴展的信息,請參考相應語言的代碼生成指南。注:擴展可 以是任何字段類型,包括消息類型。
1.嵌套的擴展
可以在另一個類型的范圍內聲明擴展,如:
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; } |
事實上,這種語法格式更能防止引起混淆。正如上面所提到的,嵌套的語法通常被錯誤地認為有子類化的關系——尤其是對那些還不熟悉擴展的用戶來說。
2.選擇可擴展的標符號
在同一個消息類型中一定要確保兩個用戶不會擴展新增相同的標識號,否則可能會導致數據的不一致。可以通過為新項目定義一個可擴展標識號規則來防止該情況的發生。
如果標識號需要很大的數量時,可以將該可擴展標符號的范圍擴大至max,其中max是2^29 - 1, 或536,870,911。如下所示:
message Foo { extensions 1000 to max; } |
通常情況下在選擇標符號時,標識號產生的規則中應該避開[19000-19999]之間的數字,因為這些已經被Protocol Buffers實現中預留了。
三、包(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模塊是按照其在文件系統中的位置進行組織的。
1.包及名稱的解析
Protocol buffer語言中類型名稱的解析與C++是一致的:首先從最內部開始查找,依次向外進行,每個包會被看作是其父類包的內部類。當然對於 (foo.bar.Baz)這樣以“.”分隔的意味着是從最外圍開始的。ProtocolBuffer編譯器會解析.proto文件中定義的所有類型名。 對於不同語言的代碼生成器會知道如何來指向每個具體的類型,即使它們使用了不同的規則。
原文
http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html