假设我有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 documentation,但无法找到办法。
答案 0 :(得分:4)
我知道这是一个老话题,但无论如何我都会回答后人:
首先,正如您所知,不可能纯粹从其序列化形式确定协议缓冲区消息的类型。您可以访问的序列化表单中唯一的信息是字段编号及其序列化值。
其次,“正确”的方法是使用包含两者的原型,如
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;
}
然后,您应该可以假装邮件(A
或B
)实际上是Shared
,并相应地解析它。未知领域将无关紧要。
答案 1 :(得分:0)
Python相对于像C ++这样的静态类型语言的一个优点是,您不需要使用任何特殊的反射代码来获取未知类型的对象的属性:您只需询问该对象。执行此操作的内置函数是getattr
,因此您可以执行以下操作:
settings_value = getattr(obj, 'CommonSettings')
答案 2 :(得分:0)
我遇到了类似的问题。
我所做的是创建一条新消息,并使用枚举指定类型:
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 ...
希望这有帮助。
答案 3 :(得分:0)
如何&#34;连接&#34;标题+有效载荷格式中的两个协议缓冲器,例如,标题,因为公共数据遵循protobuf techniques建议的消息A或B?
这就是我在mqtt消息中使用各种类型的有效负载作为blob的方式。