在Python中访问未知类型的Protobuf消息的字段



假设我有两个protobuf - message, 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()来获取我想要获得的字段的描述符。然后,它使用反射来获取实际数据。getCommonBlock可以处理类型A、B或任何未来类型的消息,只要Common字段仍然位于索引3。

我的问题是:有没有一种方法可以做类似的事情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;
   }
}

这样,就没有歧义了:每次都解析相同的原型(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 ...
}

确保正确定义'Base'消息,否则如果您最近添加字段(因为您将不得不转移继承消息字段的),您将不兼容二进制。

这样,您可以收到一个通用消息:

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 ...

如何在报头+有效载荷格式中"连接"两个协议缓冲区,例如报头作为通用数据跟随消息a或B,如protobuf技术所建议的?

这就是我如何在mqtt消息中将各种类型的有效负载作为blob。

相关内容

  • 没有找到相关文章

最新更新