如何获取protobuf消息中定义的变量类型?

时间:2015-11-11 16:41:28

标签: python protocol-buffers

我正在尝试使用Python从protobuf文件到Objective-C类进行一些“翻译”。例如,给出protobuf消息:

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
}

我想把它翻译成一个objc类:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int ID;
@property (nonatomic, copy) NSString *email; 
@end

关键是获取每个属性的名称和类型。例如,protobuf消息中的“可选字符串电子邮件”,其名称为“email”,类型为“string”,因此它应该是objective-c中的NSString * email。我按照官方教程,编写了一个addressbook.proto,与教程中的相同,并编译了它。然后我编写了我的python代码:

import addressbook_pb2 as addressbook

p = addressbook.Person()
all_fields = p.DESCRIPTOR.fields_by_name
# print "all fields: %s" %all_fields
field_keys = all_fields.keys()
# print "all keys: %s" %field_keys

for key in field_keys:
    one_field = all_fields[key]
    print one_field.label

这只是给了我:

1
2
3
2

所以我猜标签不是我需要的,而field_keys只是我期望的名字列表。我试了一些其他的话,并在网上做了一些搜索,但没有找到正确的答案。

如果无法获取该类型,我还有另外一个想法,即以纯粹的'Pythonic'方式读取和分析protobuf源文件的每一行,但我真的不想这样做,如果它没必要。

有人能帮助我吗?

2 个答案:

答案 0 :(得分:0)

感谢Marc的回答,我找到了一些解决方案。这只是一个想法,但对我来说这是一个巨大的进步。

Python代码:

import addressbook_pb2 as addressbook

typeDict = {"1":"CGFloat", "2":"CGFloat", "3":"NSInteger", "4":"NSUinteger", "5":"NSInteger", "8":"BOOL", "9":"NSString", "13":"NSUinteger", "17":"NSInteger", "18":"NSInteger"}

attrDict = {"CGFloat":"assign", "NSInteger":"assign", "NSUinteger":"assign", "BOOL":"assign", "NSString":"copy"}

p = addressbook.Person()
all_fields = p.DESCRIPTOR.fields_by_name
field_keys = all_fields.keys()
for key in field_keys:
    one_field = all_fields[key]
    typeNumStr = str(one_field.type)
    className = typeDict.get(typeNumStr, "NSObject")
    attrStr = attrDict.get(className, "retain")
    propertyStr = "@property (nonatomic, %s) %s *%s" %(attrStr, className, key)
    print propertyStr

对于地址簿示例,它会打印:

@property (nonatomic, copy) NSString *email
@property (nonatomic, copy) NSString *name
@property (nonatomic, retain) NSObject *phone
@property (nonatomic, assign) NSInteger *id

不是最终的解决方案,但它意味着很多。谢谢,Marc!

答案 1 :(得分:0)

FieldDescriptor类具有一个message_type成员,如果是复合字段,则该成员是此字段中包含的消息类型的描述符。否则,为无。

通过遍历DESCRIPTORS的字典来组合起来,这意味着您可以获得复合和非复合(原始)字段的名称类型

import addressbook_pb2 as addressbook
DESCRIPTORS = addressbook.Person.DESCRIPTOR.fields_by_name

for (field_name, field_descriptor) in DESCRIPTORS.items():

    if field_descriptor.message_type:
        # Composite field
        print(field_name, field_descriptor.message_type.name)
    else:
        # Raw type
        print(field_name, field_descriptor.type)
        # TYPE_DOUBLE
        # TYPE_FLOAT 
        # TYPE_INT64 
        # TYPE_UINT64
        # TYPE_INT32 
        # TYPE_FIXED64
        # TYPE_FIXED32
        # TYPE_BOOL  
        # TYPE_STRING
        # TYPE_GROUP
        # TYPE_MESSAGE
        # TYPE_BYTES
        # TYPE_UINT32
        # TYPE_ENUM
        # TYPE_SFIXED32
        # TYPE_SFIXED64
        # TYPE_SINT32
        # TYPE_SINT64
        # MAX_TYPE

原始类型是类属性; https://github.com/protocolbuffers/protobuf/blob/master/python/google/protobuf/descriptor.py