簡體   English   中英

在Python中訪問未知類型的Protobuf消息字段

[英]Accessing field of Protobuf message of unknown type in Python

假設我有2個Protobuf-Messages,A和B.它們的整體結構相似,但不完全相同。 所以我們將共享的東西移到一個我們稱為Common的單獨消息中。 這很好用。

但是,我現在面臨以下問題:我需要處理序列化消息的特殊情況,但我不知道它是A類型還是B類消息。我有一個C ++工作解決方案(顯示下面),但我沒能找到在Python中做同樣事情的方法。

例:

// file: Common.proto
// contains some kind of shared struct that is used by all messages:
message Common {
 ...
}

// file: A.proto
import "Common.proto";

message A {
   required int32  FormatVersion             = 1;
   optional bool   SomeFlag [default = true] = 2;
   optional Common CommonSettings            = 3;

   ... A-specific Fields ...
}

// file: B.proto
import "Common.proto";

message B {
   required int32  FormatVersion             = 1;
   optional bool   SomeFlag [default = true] = 2;
   optional Common CommonSettings            = 3;

   ... B-specific Fields ...
}

C ++中的工作解決方案

在C ++中,我使用反射API來訪問CommonSettings字段,如下所示:

namespace gp = google::protobuf;
...
Common* getCommonBlock(gp::Message* paMessage)
{
   gp::Message* paMessage = new gp::Message();
   gp::FieldDescriptor* paFieldDescriptor = paMessage->GetDescriptor()->FindFieldByNumber(3);
   gp::Reflection* paReflection = paMessage->GetReflection();
   return dynamic_cast<Common&>(paReflection->GetMessage(*paMessage,paFieldDescriptor));
}

方法' getCommonBlock '使用FindFieldByNumber()來獲取我想要獲取的字段的描述符。 然后它使用反射來獲取實際數據。 只要Common字段仍位於索引3, getCommonBlock就可以處理A,B類型或任何未來類型的消息。

我的問題是:有沒有辦法做類似的Python? 我一直在看Protobuf文檔 ,但無法找到一種方法。

我知道這是一個老話題,但無論如何我會回應后人:

首先,如您所知,不可能純粹從其序列化形式確定協議緩沖消息的類型。 您可以訪問的序列化表單中唯一的信息是字段編號及其序列化值。

其次,“正確”的方法是制作包含兩者的原型,例如

message Parent {
   required int32  FormatVersion             = 1;
   optional bool   SomeFlag [default = true] = 2;
   optional Common CommonSettings            = 3;

   oneof letters_of_alphabet {
      A a_specific = 4;
      B b_specific = 5;
   }
}

這樣,就沒有含糊之處:你每次只解析相同的proto( Parent )。


無論如何,如果改變它已經太晚了,我建議你做的是定義一個只有共享字段的新消息,比如

message Shared {
   required int32  FormatVersion             = 1;
   optional bool   SomeFlag [default = true] = 2;
   optional Common CommonSettings            = 3;
}

然后,您應該能夠假裝消息( AB )實際上是Shared ,並相應地解析它。 未知領域將無關緊要。

Python相對於像C ++這樣的靜態類型語言的一個優點是,您不需要使用任何特殊的反射代碼來獲取未知類型的對象的屬性:您只需要詢問該對象。 執行此操作的內置函數是getattr ,因此您可以執行以下操作:

settings_value = getattr(obj, 'CommonSettings')

我遇到了類似的問題。

我所做的是創建一個新消息,枚舉指定類型:

enum TYPE {
  A = 0;
  B = 1;
}
message Base {
  required TYPE type = 1;
  ... Other common fields ...
}

然后創建特定的消息類型:

message A {
  required TYPE type = 1 [default: A];
  ... other A fields ...
}

和:

message B {
  required TYPE type = 1 [default: B];
  ... other B fields ...
}

確保正確定義“基本”消息,或者如果最近添加字段,則不會是二進制兼容的(因為您也必須轉移繼承消息字段)。

這樣,您可以收到一般信息:

msg = ... receive message from net ...

# detect message type
packet = Base()
packet.ParseFromString(msg)

# check for type
if packet.type == TYPE.A:
    # parse message as appropriate type
    packet = A()
    packet.ParseFromString(msg)
else:
    # this is a B message... or whatever

# ... continue with your business logic ...

希望這可以幫助。

如何在標頭+有效載荷格式中“連接”兩個協議緩沖區,例如標頭作為公共數據,遵循protobuf技術建議的消息A或B?

這就是我在mqtt消息中使用各種類型的有效負載作為blob的方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM