如何将已编译的协议缓冲区转换回 .proto 文件?

How to convert a compiled protocol buffer back to .proto file?

我有一个为 python 2 编译的 google 协议缓冲区,我正试图将其移植到 python 3。不幸的是,我找不到我以前使用的原型文件在任何地方生成编译的协议缓冲区。如何恢复原始文件以便为 python 编译一个新文件 3. 我不知道使用了哪些原始版本,我所拥有的只是用于 运行 的 .py 文件python2.6.

您将不得不编写代码(例如在 Python 中)遍历消息描述符树。原则上,它们应该包含原始原型文件的全部信息,但代码注释除外。生成的 Python 模块你仍然拥有你的 posession 应该允许你将你的原型文件的文件描述符序列化为文件描述符原型消息,然后可以将其提供给将其表达为原型代码的代码。

作为指南,您应该查看 protoc 的各种代码生成器,它们实际上做同样的事情:它们将文件描述符作为 protobuf 消息读取,分析它并生成代码。

这里简单介绍下如何在Python

中编写Protobuf插件

https://www.expobrain.net/2015/09/13/create-a-plugin-for-google-protocol-buffer/

这是协议插件的官方列表

https://github.com/google/protobuf/blob/master/docs/third_party.md

这是一个生成 LUA 代码的协议插件,用 Python.

编写

https://github.com/sean-lin/protoc-gen-lua/blob/master/plugin/protoc-gen-lua

让我们看一下主要代码块

def main():
    plugin_require_bin = sys.stdin.read()
    code_gen_req = plugin_pb2.CodeGeneratorRequest()
    code_gen_req.ParseFromString(plugin_require_bin)

    env = Env()
    for proto_file in code_gen_req.proto_file:
        code_gen_file(proto_file, env,
                      proto_file.name in code_gen_req.file_to_generate)

    code_generated = plugin_pb2.CodeGeneratorResponse()
    for k in  _files:
        file_desc = code_generated.file.add()
        file_desc.name = k
        file_desc.content = _files[k]

    sys.stdout.write(code_generated.SerializeToString())

循环 for proto_file in code_gen_req.proto_file: 实际上循环遍历文件描述符对象,protoc 要求代码生成器插件为其生成 LUA 代码。 所以现在你可以这样做:

# This should get you the file descriptor for your proto file
file_descr = your_package_pb2.sometype.GetDescriptor().file
# serialized version of file descriptor
filedescr_msg = file_descr.serialized_pb
# required by lua codegen
env = Env()
# create LUA code -> modify it to create proto code
code_gen_file(filedescr, env, "your_package.proto")

如其他 post(s) 中所述,您需要遍历描述符消息树并构建原型文件内容。

您可以在协议缓冲区中找到完整的 C++ 示例 github repository。以下是 link 中的一些 C++ 代码片段,以便让您了解如何在 Python 中实现它:

  // Special case map fields.
  if (is_map()) {
    strings::SubstituteAndAppend(
        &field_type, "map<[=10=], >",
        message_type()->field(0)->FieldTypeNameDebugString(),
        message_type()->field(1)->FieldTypeNameDebugString());
  } else {
    field_type = FieldTypeNameDebugString();
  }

  std::string label = StrCat(kLabelToName[this->label()], " ");

  // Label is omitted for maps, oneof, and plain proto3 fields.
  if (is_map() || containing_oneof() ||
      (is_optional() && !has_optional_keyword())) {
    label.clear();
  }

  SourceLocationCommentPrinter comment_printer(this, prefix,
                                               debug_string_options);
  comment_printer.AddPreComment(contents);

  strings::SubstituteAndAppend(
      contents, "[=10=]  = ", prefix, label, field_type,
      type() == TYPE_GROUP ? message_type()->name() : name(), number());

其中FieldTypeNameDebugString函数如下所示:

// The field type string used in FieldDescriptor::DebugString()
std::string FieldDescriptor::FieldTypeNameDebugString() const {
  switch (type()) {
    case TYPE_MESSAGE:
      return "." + message_type()->full_name();
    case TYPE_ENUM:
      return "." + enum_type()->full_name();
    default:
      return kTypeToName[type()];
  }
}