繁体   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