【问题标题】:Extracting Protobuf custom option from file descriptor set?从文件描述符集中提取 Protobuf 自定义选项?
【发布时间】:2021-03-08 17:35:24
【问题描述】:

假设我们有一个名为foo.proto 的文件定义了一条消息和一个custom option

syntax = "proto3";

package foo_package;

import "google/protobuf/descriptor.proto";

enum State {
    ALPHA = 0;
    BETA = 1;
}

extend google.protobuf.FieldOptions {
    State baz = 51234;
}

message Foo {
    string bar = 1 [ (baz) = ALPHA ];
}                                                                                                                                            

我们通过以下方式为此消息生成FileDescriptorSet(从包含google/protobuf/descriptor.proto 的目录):

protoc -I=. --include_imports -oTMP ./foo.proto

如何从该集合中的消息类实例中提取 baz 文档和库 (1)(2) 建议这样的方法可能有效:

from google.protobuf.descriptor_pb2 import FileDescriptorSet
from google.protobuf.message_factory import GetMessages

with open("TMP", mode="rb") as f:
    fds = FileDescriptorSet.FromString(f.read())

messages = GetMessages([file for file in fds.file])
extensions = messages["foo_package.Foo"].DESCRIPTOR.fields_by_name["bar"].GetOptions().Extensions

但生成的对象是空的。 #6662 暗示使用 DescriptorPool 应该可以解决它,但这似乎也不起作用(空对象也是如此)。

【问题讨论】:

  • 我偶然发现这个问题正在寻找其他东西,但最近也遇到了这个问题并想通了!会写一个答案。

标签: python protocol-buffers proto


【解决方案1】:

GetOptions() 将为您提供内置的非扩展类型google.protobuf.FieldOptions。在 protobuf 实现中,GetOptions() 构造了一个内置类型 google.protobuf.FieldOptions 的消息,然后将字节解析到该结构中。你可以在 Python impl 中看到这一点,例如:https://github.com/protocolbuffers/protobuf/blob/5df4c2ec9426b06dfe8a019ddcf1509b8816cebe/python/google/protobuf/descriptor.py#L167-L170

消息描述符确实具有您定义的选项;您可以打印字段选项的序列化版本,您会看到它们,但它们没有被解析为结果字段选项。非扩展类型不描述那些额外的字段,所以它们被忽略了。

你真正想要的是使用google.protobuf.FieldOptions你的扩展版本来解析选项。

我不确定是否有更优雅的方法来执行此操作,但对我有用的是重新序列化选项,然后使用扩展 FieldOptions 的动态版本解析它们。总体看起来像这样:

messages = GetMessages([file for file in fds.file])
Foo = messages["foo_package.Foo"]
FieldOptions = messages["google.protobuf.FieldOptions"]

bar_builtin_opts = Foo.DESCRIPTOR.fields_by_name["bar"].GetOptions()
bar_opts = FieldOptions()
bar_opts.ParseFromString(bar_builtin_opts.SerializeToString())

请注意,此过程对于所有选项类型(字段选项、消息选项等)都是相同的。

如果您在重新定义现有消息类型时遇到问题,您可能必须创建自己的描述符池和消息工厂,该描述符池和消息工厂不同于默认的,尽管这很简单

pool = DescriptorPool()
for fd in fds.file:
    pool.Add(fd)
factory = MessageFactory(pool)
messages = factory.GetMessages([file.name for file in fds.file])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 2019-10-02
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多