2013-05-17 92 views
7

假设我有2个Protobuf消息,A和B.它们的总体结构相似,但不完全相同。所以我们把共享的东西放到了一个我们称为Common的单独消息中。这工作很好。在Python中访问未知类型Protobuf消息的字段

但是,我现在面临以下问题:存在一个特殊情况,我必须处理序列化消息,但我不知道它是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'我们es FindFieldByNumber()获取我试图获取的字段的描述符。然后它使用反射来获取实际数据。 getCommonBlock只要公共字段保持位于索引3,就可以处理类型A,B或任何未来类型的消息。

我的问题是:有没有办法做类似的事情Python?我一直在寻找Protobuf documentation,但无法想出办法。

回答

3

我知道这是一个古老的线程,但我会为后人反正回应:

首先,你知道,这是不可能的,从它的序列化形式纯粹确定的协议缓冲消息的类型。您可以访问的序列化表单中的唯一信息是字段编号及其序列化值。

其次,“正确”的方式做这将是具有同时包含原,像

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,并相应地解析它。未知领域将无关紧要。

0

对于像C++这样的静态类型语言,Python的优点之一是不需要使用任何特殊的反射代码来获取未知类型的对象的属性:只需询问对象即可。内置的函数,它这是getattr,所以你可以做:

settings_value = getattr(obj, 'CommonSettings') 
+5

这将工作,如果我有一个obj的实例。也许我应该澄清一下我的问题:我以典型的protobuf方式接收消息,作为序列化的blob(二进制内存流)。如何在不知道底层消息类型的情况下实例化obj? – djf

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

一定要正确定义“基本”信息,否则如果您最近添加字段,您将不会是二进制兼容的(因为您将不得不移动也继承消息字段)。

这样的话,你可以recive一个通用的消息:

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

希望这有助于。

+0

我在消息A和B中看到“Type”。难道你不想在这些地方放置“Base”吗? – deddebme

+0

如果正确导入,TYPE是protobuf文件中定义的枚举,因此您可以直接调用它。 – kraptor

0

如何在报头+有效载荷格式中“连接”两个协议缓冲区,例如,标题为公共数据,如protobuf techniques建议的消息A或B所示?

这就是我在mqtt消息中以不同类型的有效载荷作为blob做到的。