Google Protocol Buffer 的常规用法需要使用 protoc.proto 编译成 .pb.h.pb.cc,这样做效率非常高,但是耦合性也很高。在某些追求通用性而不追求性能的场景下,需要使用 .proto 直接操作 protobuf 数据。

本例使用的 .proto 文件来自 https://developers.google.com/protocol-buffers/docs/cpptutorial ,但是把它拆成了两个 .proto 文件

  1. // ./proto/person.proto
  2. syntax = "proto2";
  3. package tutorial;
  4. message Person {
  5. optional string name = 1;
  6. optional int32 id = 2;
  7. optional string email = 3;
  8. enum PhoneType {
  9. MOBILE = 0;
  10. HOME = 1;
  11. WORK = 2;
  12. }
  13. message PhoneNumber {
  14. optional string number = 1;
  15. optional PhoneType type = 2 [default = HOME];
  16. }
  17. repeated PhoneNumber phones = 4;
  18. }
  1. // ./proto/person.proto
  2. syntax = "proto2";
  3. package tutorial;
  4. import "person.proto";
  5. message AddressBook {
  6. repeated Person people = 1;
  7. }=

示例代码

  1. #include <iostream>
  2. #include <google/protobuf/compiler/importer.h>
  3. #include <google/protobuf/dynamic_message.h>
  4. #include <google/protobuf/util/json_util.h>
  5. using namespace google::protobuf;
  6. /*
  7. 构造 Importer 必须指定 error_collector 用于处理错误信息
  8. AddError 是纯虚函数,必须 override
  9. */
  10. class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector
  11. {
  12. virtual void AddError(const std::string& filename, int line, int column, const std::string& message) override
  13. {
  14. std::cout << "file: " << filename << ", line: " << line << ", col: " << column<< ", message: " << message << std::endl;
  15. }
  16. };
  17. int main()
  18. {
  19. /*
  20. 构造 DiskSourceTree,并添加虚拟路径。protobuf 使用 Importor 导入 .proto 文件时,会虚拟路径进行查找
  21. 在本例中,导入 addressbook.proto 时会使用 ./proto/addressbook.proto
  22. */
  23. compiler::DiskSourceTree disk_source_tree;
  24. disk_source_tree.MapPath("", "proto");
  25. MyMultiFileErrorCollector error_collector;
  26. /*
  27. 导入 addressbook.proto 时,会自动导入所有依赖的 .proto 文件
  28. 在本例中,person.proto 也会被自动导入
  29. */
  30. compiler::Importer importer(&disk_source_tree, &error_collector);
  31. const FileDescriptor* file_descriptor = importer.Import("addressbook.proto");
  32. if (!file_descriptor) {
  33. exit(-1);
  34. }
  35. // 把 addressbook.proto 和 person.proto 都打印出来
  36. std::cout << "====== all .proto files ======" << std::endl;
  37. std::cout << file_descriptor->DebugString() << std::endl;
  38. for (int i = 0; i < file_descriptor->dependency_count(); ++i) {
  39. std::cout << file_descriptor->dependency(i)->DebugString() << std::endl;
  40. }
  41. // 构造 DynamicMessageFactory 用于动态构造 Message
  42. DynamicMessageFactory message_factory(importer.pool()->generated_pool());
  43. /*
  44. 查找 Person 的 Descriptor
  45. 不能使用 file_descriptor 查找,它只包含 addresssbook.proto ,只能找到 AddressBook,而 DescriptorPool 包含了所有数据
  46. 在使用 DescriptorPool 查找时需要使用全名,如:tutorial.Person
  47. 在使用 FileDescritor 查找需要使用顶级名字,如 AddressBook,而不是 tutorial.AddressBook
  48. */
  49. const Descriptor* person_descriptor = importer.pool()->FindMessageTypeByName("tutorial.Person");
  50. /*
  51. 使用工厂创建默认 Message,然后构造一个可以用来修改的 Message
  52. 这个 Message 的生命周期由 New 调用者管理
  53. */
  54. const Message* default_person = message_factory.GetPrototype(person_descriptor);
  55. Message* person = default_person->New();
  56. // 使用 Reflection 修改 Message 的数据
  57. const Reflection* reflection = person->GetReflection();
  58. reflection->SetString(person, person_descriptor->FindFieldByName("name"), "abc");
  59. reflection->SetInt32(person, person_descriptor->FindFieldByName("id"), 123456);
  60. reflection->SetString(person, person_descriptor->FindFieldByName("email"), "abc@163.com");
  61. // 把动态设置的 Message 的数据以 JSON 格式输出
  62. util::JsonPrintOptions json_options;
  63. json_options.add_whitespace = true;
  64. json_options.preserve_proto_field_names = true;
  65. string output;
  66. util::MessageToJsonString(*person, &output, json_options);
  67. std::cout << "====== Person data ======" << std::endl;
  68. std::cout << output;
  69. // 析构 person
  70. delete person;
  71. }

输出

  1. ====== all .proto files ======
  2. syntax = "proto2";
  3. import "person.proto";
  4. package tutorial;
  5. message AddressBook {
  6. repeated .tutorial.Person people = 1;
  7. }
  8. syntax = "proto2";
  9. package tutorial;
  10. message Person {
  11. message PhoneNumber {
  12. optional string number = 1;
  13. optional .tutorial.Person.PhoneType type = 2 [default = HOME];
  14. }
  15. enum PhoneType {
  16. MOBILE = 0;
  17. HOME = 1;
  18. WORK = 2;
  19. }
  20. optional string name = 1;
  21. optional int32 id = 2;
  22. optional string email = 3;
  23. repeated .tutorial.Person.PhoneNumber phones = 4;
  24. }
  25. ====== Person data ======
  26. {
  27. "name": "abc",
  28. "id": 123456,
  29. "email": "abc@163.com"
  30. }

https://developers.google.com/protocol-buffers/docs/reference/cpp

Protobuf 动态加载 .proto 文件并操作 Message的更多相关文章

  1. Protobuf 动态加载 .pb 文件并操作 Message

    之前写了<Protobuf 动态加载 .proto 文件并操作 Message>.除了直接读取 .proto 文件之外,还有一种类似的方法.先把 .proto 文件编译成 .pb 文件,再 ...

  2. Java_Java中动态加载jar文件和class文件

    转自:http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

  3. 动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数

    动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数, 在很多场景下,我们需要在动态加载JS文件的时候,根据加载的状态来进行后续的操作,需要在JS加载成功后,执行另一方法,这个方法是依托在加 ...

  4. [转载] Java中动态加载jar文件和class文件

    转载自http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

  5. javascript动态加载js文件主流浏览器兼容版

    一.代码示例: <html> <head> <meta http-equiv="Content-Type" content="text/ht ...

  6. Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)

    Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1.       内联样式 ...

  7. Android应用安全之外部动态加载DEX文件风险

    1. 外部动态加载DEX文件风险描述 Android 系统提供了一种类加载器DexClassLoader,其可以在运行时动态加载并解释执行包含在JAR或APK文件内的DEX文件.外部动态加载DEX文件 ...

  8. js动态加载css文件和js文件的方法

    今天研究了下js动态加载js文件和css文件的方法. 网上发现一个动态加载的方法.摘抄下来,方便自己以后使用 [code lang="html"] <html xmlns=& ...

  9. 两种动态加载JavaScript文件的方法

    两种动态加载JavaScript文件的方法 第一种便是利用ajax方式,第二种是,动静创建一个script标签,配置其src属性,经过把script标签拔出到页面head来加载js,感乐趣的网友可以看 ...

随机推荐

  1. Python基础笔记3

    高级特性 代码不是越多越好,而是越少越好.代码不是越复杂越好,而是越简单越好.代码越少,开发效率越高. 1.切片 切片(Slice)操作符,取一个list或tuple的部分元素非常常见. 列表 L = ...

  2. expr判断文件名以固定格式结尾

    #!/bin/bash if expr "$1" : ".*\.sh" &>/dev/null then echo "okok" ...

  3. 大规模 K8s 集群管理经验分享 · 上篇

    11 月 23 日,Erda 与 OSCHINA 社区联手发起了[高手问答第 271 期 -- 聊聊大规模 K8s 集群管理],目前问答活动已持续一周,由 Erda SRE 团队负责人骆冰利为大家解答 ...

  4. C++ 德才论

    输入样例: 14 60 80 10000001 64 90 10000002 90 60 10000011 85 80 10000003 85 80 10000004 80 85 10000005 8 ...

  5. 【Java 泛型】之 <? super T> 和<? extends T> 中 super ,extends如何理解?有何异同?

    Java 泛型 <? super T> 和<? extendsT>中 super ,extends怎么 理解?有何不同? 简介 前两篇文章介绍了泛型的基本用法.类型擦除以及泛型 ...

  6. LINUX 系统性能检测工具vmstat

    vmstat 有2个参数,第一个是采样时间间隔(单位是s),第二个参数是采样个数. #表示 2s采样一次,一共采样2次 vmstat 2 2 也可以只写第一个参数,让系统一直采样直到停止(ctrl + ...

  7. Output of C++ Program | Set 6

    Predict the output of below C++ programs. Question 1 1 #include<iostream> 2 3 using namespace ...

  8. Linux安装软件出错

    1.Delta RPMs disabled because /usr/bin/applydeltarpm not installed. yum provides '*/applydeltarpm' # ...

  9. C++ friend详解

    私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行.这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦. C++ 是 ...

  10. web端 - 返回上一步,点击返回,跳转上个页面 JS

    1.方法一: <script language="javascript" type="text/javascript"> window.locati ...