Protobuf 动态加载 .proto 文件并操作 Message
2021-12-25 23:08
579 查看
Google Protocol Buffer 的常规用法需要使用
protoc将
.proto编译成
.pb.h和
.pb.cc,这样做效率非常高,但是耦合性也很高。在某些追求通用性而不追求性能的场景下,需要使用
.proto直接操作 protobuf 数据。
本例使用的
.proto文件来自 https://developers.google.com/protocol-buffers/docs/cpptutorial ,但是把它拆成了两个
.proto文件
// ./proto/person.proto syntax = "proto2"; package tutorial; message Person { optional string name = 1; optional int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { optional string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phones = 4; }
// ./proto/person.proto syntax = "proto2"; package tutorial; import "person.proto"; message AddressBook { repeated Person people = 1; }=
示例代码
#include <iostream> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/dynamic_message.h> #include <google/protobuf/util/json_util.h> using namespace google::protobuf; /* 构造 Importer 必须指定 error_collector 用于处理错误信息 AddError 是纯虚函数,必须 override */ class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector { virtual void AddError(const std::string& filename, int line, int column, const std::string& message) override { std::cout << "file: " << filename << ", line: " << line << ", col: " << column<< ", message: " << message << std::endl; } }; int main() { /* 构造 DiskSourceTree,并添加虚拟路径。protobuf 使用 Importor 导入 .proto 文件时,会虚拟路径进行查找 在本例中,导入 addressbook.proto 时会使用 ./proto/addressbook.proto */ compiler::DiskSourceTree disk_source_tree; disk_source_tree.MapPath("", "proto"); MyMultiFileErrorCollector error_collector; /* 导入 addressbook.proto 时,会自动导入所有依赖的 .proto 文件 在本例中,person.proto 也会被自动导入 */ compiler::Importer importer(&disk_source_tree, &error_collector); const FileDescriptor* file_descriptor = importer.Import("addressbook.proto"); if (!file_descriptor) { exit(-1); } // 把 addressbook.proto 和 person.proto 都打印出来 std::cout << "====== all .proto files ======" << std::endl; std::cout << file_descriptor->DebugString() << std::endl; for (int i = 0; i < file_descriptor->dependency_count(); ++i) { std::cout << file_descriptor->dependency(i)->DebugString() << std::endl; } // 构造 DynamicMessageFactory 用于动态构造 Message DynamicMessageFactory message_factory(importer.pool()->generated_pool()); /* 查找 Person 的 56c Descriptor 不能使用 file_descriptor 查找,它只包含 addresssbook.proto ,只能找到 AddressBook,而 DescriptorPool 包含了所有数据 在使用 DescriptorPool 查找时需要使用全名,如:tutorial.Person 在使用 FileDescritor 查找需要使用顶级名字,如 AddressBook,而不是 tutorial.AddressBook */ const Descriptor* person_descriptor = importer.pool()->FindMessageTypeByName("tutorial.Person"); /* 使用工厂创建默认 Message,然后构造一个可以用来修改的 Message 这个 Message 的生命周期由 New 调用者管理 */ const Message* default_person = message_factory.GetPrototype(person_descriptor); Message* person = default_person->New(); // 使用 Reflection 修改 Message 的数据 const Reflection* reflection = person->GetReflection(); reflection->SetString(person, person_descriptor->FindFieldByName("name"), "abc"); reflection->SetInt32(person, person_descriptor->FindFieldByName("id"), 123456); reflection->SetString(person, person_descriptor->FindFieldByName("email"), "abc@163.com"); // 把动态设置的 Message 的数据以 JSON 格式输出 util::JsonPrintOptions json_options; json_options.add_whitespace = true; json_options.preserve_proto_field_names = true; string output; util::Message ad8 ToJsonString(*person, &output, json_options); std::cout << "====== Person data ======" << std::endl; std::cout << output; // 析构 person delete person; }
输出
====== all .proto files ====== syntax = "proto2"; import "person.proto"; package tutorial; message AddressBook { repeated .tutorial.Person people = 1; } syntax = "proto2"; package tutorial; message Person { message PhoneNumber { optional string number = 1; optional .tutorial.Person.PhoneType type = 2 [default = HOME]; } enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } optional string name = 1; optional int32 id = 2; optional string email = 3; repeated .tutorial.Person.PhoneNumber phones = 4; } ====== Person data ====== { "name": "abc", "id": 123456, "email": "abc@163.com" }
https://developers.google.com/protocol-buffers/docs/reference/cpp
相关文章推荐
- c#networkcomms protobuf-net 文件加载出现问题
- 使用protobuf的反射来动态生成Message并赋值
- Kotlin通过Id操作View,Adapter和动态加载Xml文件也可以类似操作
- Unity--动态加载网络图片和本地图片文件
- 根据环境变量配置,动态加载配置文件(区分开发环境,生成环境)
- 实现ibatis的动态加载sqlmap配置文件 .
- Intel平台下Linux中ELF文件动态链接的加载、解析及实例分析(一):
- drupal 7 动态加载模块include文件
- android 动态加载sd卡的jar文件
- Java中动态加载properties文件,而不需要重启应用的解决方法
- 32.Struts2_通过超链接动态加载国际化资源文件
- Linux C动态加载SO库文件
- PHP动态地创建属性和方法, 对象的复制, 对象的比较,加载指定的文件,自动加载类文件,命名空间
- 解决VC中动态加载DLL时,必须将DLL文件放在EXE文件路经和系统目录下的问题
- jquery动态加载js/css文件方法
- C#读取配置文件详解 完全自定义实现动态加载dll
- 动态(按需)加载js和css文件
- 动态库连接器–动态库链接信息(Mach-O文件格式和程序从加载到执行过程)
- VC++ 动态创建单个工具条,并加载外部的位图(bmp)文件为工具栏图像
- 用jsp:include指令动态加载文件