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

Posted

技术标签:

【中文标题】如何获取 protobuf 消息中定义的变量类型?【英文标题】:How to get the type of a variable defined in a protobuf message? 【发布时间】:2015-11-11 16:41:28 【问题描述】:

我正在尝试使用 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消息中的'optional string email',它的名字是'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

所以我猜 label 不是我需要的,而 field_keys 只是我期望的名称列表。我尝试了一些其他词,并在网络上进行了一些搜索,但没有找到正确的答案。

如果没有办法获取类型,我还有一个想法,就是以纯“Pythonic”的方式读取和分析protobuf源文件的每一行,但如果它真的不想这样做没必要。

谁能帮帮我?

【问题讨论】:

你所说的“类型”是什么意思?你期待什么样的事情?有一个“wire type”描述了数据是如何打包的,并且可能有“proto”声明的类型,以及您选择的平台的运行时类型......你的意思是什么? “type”是指protobuf消息中的“string, int32”,名称是“name, id, email”。例如,我想获取“字符串”类型,将其转换为 objecitve-c 中的“NSString”。 没有type 某种成员吗?与c 版本相比:developers.google.com/protocol-buffers/docs/reference/cpp/… 谢谢!为什么我没有找到这个?我猜是因为我是 protobuf 的新手,而 google 的 python protobuf 文档比 C++ 版本丑得多。 【参考方案1】:

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

将此与遍历DESCRIPTORS 的字典相结合意味着您可以获得复合和非复合(原始)字段的nametype

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

【讨论】:

【参考方案2】:

感谢 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

不是最终解决方案,但意义重大。谢谢你,马克!

【讨论】:

以上是关于如何获取 protobuf 消息中定义的变量类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何确定protobuf中的消息类型,以便我可以使用该类型.parsefrom(byte [])

protobuf 怎么查看版本

定义了protobuf-net序列化所需的proto2消息类型?

检查 protoBuf 中是不是存在消息类型

Protobuf语法介绍

如何停止使用 protobuf3 打印错误消息“无法解析类型的消息,因为它缺少必填字段”