获取protobuf extensions内容的方法

protobuf 字段内定有extensions字段的时候,用proto的类方法无法读取字段里的内容。在stackflow中找到了解决的方法,标记一下:
I have a build system that has to pack protobufs from users’ inputs. The user fills in fields in a form, submits them, and the build system packs the protobufs and sends them along their way. The build system has to be unaware of the types it is processing. Otherwise, every time the project team changed their .protos, I’d have to make build changes to support them.
The vast majority of this works. The .protos might look like this:

#dataobj_base.proto
message DataObj {
extensions 100 to 199;
optional string property1 = 1;
}

#dataobj.proto
message DataObjExtension {
optional string ext_property1 = 1;
}

extend DataObj {
optional DataObjExtension generic_extension = 100;
}

…and this is simple enough to support. Up to this point, my build system has accessed the generic_extension object by knowing the name of the type it is writing, and nothing more. I could import DataObj_pb2 and DataObjExtension_pb2 and use them like this:

def do_the_thing(type_name, property1, ext_property1):
#type_name, in this case, is ‘DataObj’
game_module = (
importlib.import_module(‘generated_protobufs.{}_pb2’.format(type_name)))
ext_module = (
importlib.import_module(‘generated_protobufs.{}Extension_pb2’.format(type_name)))
instance_constructor = getattr(game_module, type_name)
instance = instance_constructor()

#instance is now an instance of DataObj
instance.property1 = property1
setattr(pb_instance.Extensions[ext_module.generic_extension],
property_name, property_value)
}
That works great, but the project team want to derive multiple types from the same base. It breaks if I need two different types that work with the DataObj “base.” I’d like to be able to do this:

extend DataObj {
optional SomeOtherBaseObj generic_extension = 100;
}
The trouble is that, in my python code, I need to know the type of generic_extension. If I were writing the code with knowledge of the type I was processing, I would do:

extensions = instance.Extensions[SomeOtherBaseObj_pb2.generic_extension]
…and extensions.class would tell me everything I need to know.

Is there a way to get the names of any of the properties in the extension without first knowing anything about them - including (especially) their type info? There are a million ways that I could redesign the .protos to be a huge improvement, but there’s already a considerable base of code that depends on them as they are. Adding fields would be fine, and I might be able to sell minor modifications, but large structural changes aren’t possible.

Even if I could just get a list of the properties in the extension, I’d be golden. I’d hack it up like this:

extend DataObj {
optional SomeOtherBaseObj generic_extension = 100;
optional bool generic_extension_type__SomeOtherBaseObj = 101;
}
The generic_extension_type__… would be there for no reason, other than to give me the name of the type for generic_extension. Though, if I could do that, I could simplify that to:

extend DataObj {
optional SomeOtherBaseObj generic_extension__SomeOtherBaseObj = 100;
}

The ANSWER:
instance.ListFields() list all fields set on the message instance, including extensions. Each item in the list is a tuple of a FieldDescriptor – which contains full type information – and the field value. (The FieldDescriptor is also the thing you’d use as a key into the Extensions map.) So you can do:

for field, value in instance.ListFields():
assert value == instance.Extensions[field]
print field.full_name, value
If you want to find extensions that aren’t already in the message, you have a few options:

DataObj._extensions_by_name is a dict mapping fully-qualified Protobuf symbol names (like my_pkg.generic_extension) to FieldDescriptors for the corresponding extensions.

DataObj._extensions_by_number is a dict mapping field numbers (100 to 199 in your case) to FieldDescriptors for the corresponding extensions.

You can of course iterate over either dict to learn about all known extensions.

Unfortunately, both of these are technically private, meaning they could break in a future release. There is no public interface at present AFAIK. However, these members have existed since the first release and exist in both the pure-Python and C-extension-backed implementations. Moreover, the entire “extensions” feature has been removed in proto3, so worrying about this interface going away seems moot.

Also note that it’s considered not-great design to rely on these “global registries”. A better design would be for the code which initializes your component to pass in a list of all extensions which it needs to know about. Some high-level code should poll other components of the program to ask them what extensions they need, build an exhaustive list, and then pass that in. This approach makes it easier to know which extensions are actually used somewhere vs. which ones are “dead code” that can be removed. However, I recognize that existing codebases aren’t always so well-written and so implementing things “the right way” can be more trouble than it is worth.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值