C++实用技术 - protobuf动态解析proto

sylar
2019.08.02 16:43:29阅读 4点赞 0收藏 0更多
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

C++实用技术 - protobuf动态解析proto

利用protobuf的反射功能,我们可以解析任意message的任意字段信息。
有时候我们可能需要动态加载proto文件,利用新加载进来的proto文件的信息去解析数据。并把数据输出或者转格式
这时候我们就需要利用到protobuf的google::protobuf::compiler::DiskSourceTree类去加载proto文件。
利用google::protobuf::compiler::Importer类去导入proto文件

实现代码

测试用的proto文件

//sylar.proto package sylar; message AA { optional string name = 1; optional int32 age = 2; } message Test { optional string name = 1; optional int32 age = 2; repeated string phones = 3; repeated AA aa = 4; } message XX { optional string name = 1; repeated AA aa = 4; }

动态解析proto文件代码

int DynamicParseFromPBFile(const std::string& filename , const std::string& classname , std::function<void(::google::protobuf::Message* msg)> cb) { //TODO 检查文件名是否合法 auto pos = filename.find_last_of('/'); std::string path; std::string file; if(pos == std::string::npos) { file = filename; } else { path = filename.substr(0, pos); file = filename.substr(pos + 1); } ::google::protobuf::compiler::DiskSourceTree sourceTree; sourceTree.MapPath("", path); ::google::protobuf::compiler::Importer importer(&sourceTree, NULL); importer.Import(file); const ::google::protobuf::Descriptor *descriptor = importer.pool()->FindMessageTypeByName(classname); if(!descriptor) { return 1; } ::google::protobuf::DynamicMessageFactory factory; const ::google::protobuf::Message *message = factory.GetPrototype(descriptor); if(!message) { return 2; } ::google::protobuf::Message* msg = message->New(); if(!msg) { return 3; } cb(msg); delete msg; return 0; }

动态解析proto string

int DynamicParseFromPBString(const std::string& proto_string , const std::string& classname , std::function<void(::google::protobuf::Message* msg)> cb) { std::stringstream ss; ss << "/tmp/dps_" << rand() << "_" << rand() << ".proto"; std::ofstream ofs(ss.str()); ofs << proto_string; ofs.close(); return DynamicParseFromPBFile(ss.str(), classname, cb); }

测试代码

#include <google/protobuf/message.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/dynamic_message.h> #include <google/protobuf/compiler/importer.h> #include "sylar.pb.h" #include <functional> #include <iostream> #include <fstream> #include <string> #include <sstream> //filename proto文件名 //classname proto文件里面的class, 格式package.Classname //cb, 创建好proto文件里面的classname类之后执行的回调,可以在该回调里面操作数据 int DynamicParseFromPBFile(const std::string& filename , const std::string& classname , std::function<void(::google::protobuf::Message* msg)> cb); //proto_string proto文件的内容 //classname proto文件里面的class, 格式package.Classname //cb, 创建好proto文件里面的classname类之后执行的回调,可以在该回调里面操作数据 int DynamicParseFromPBString(const std::string& proto_string , const std::string& classname , std::function<void(::google::protobuf::Message* msg)> cb); int main(int argc, char** argv) { //创建一个测试类,并进行初始化 sylar::Test test; test.set_name("test_name"); test.set_age(100); test.add_phones("138xxxxxx"); test.add_phones("139xxxxxx"); for(int i = 0; i < 3; ++i) { auto* a = test.add_aa(); a->set_name("a_name_" + std::to_string(i)); a->set_age(100 + i); } std::string pb_str; //把测试类,序列化到string中,以供后续解析 test.SerializeToString(&pb_str); //打印测试类的数据信息 std::cout << test.DebugString() << std::endl; std::cout << "===============================" << std::endl; //从文件中sylar.proto中的sylar.XX类,解析上面测试类的序列化二进制 //并输出序列化后的sylar.XX的信息 DynamicParseFromPBFile("sylar.proto", "sylar.XX" , [pb_str](::google::protobuf::Message* msg) { if(msg->ParseFromString(pb_str)) { std::cout << msg->DebugString() << std::endl; } }); std::cout << "===============================" << std::endl; //从文件中sylar.proto中的sylar.Test,解析上面测试类的序列化二进制 //并输出序列化后的sylar.Test的信息 DynamicParseFromPBFile("sylar.proto", "sylar.Test" , [pb_str](::google::protobuf::Message* msg) { if(msg->ParseFromString(pb_str)) { std::cout << msg->DebugString() << std::endl; } }); //字符串pb文件内容 std::string pbstr = "package xx;\n" "message BB { \n" " optional string name = 1; \n" " optional int32 age = 2; \n" "} \n" "message TT { \n" " optional string name = 1; \n" " optional int32 age = 2; \n" " repeated string phones = 3; \n" " repeated BB aa = 4; \n" "}"; std::cout << "===============================" << std::endl; //pbstr的proto信息中的xx.TT,解析上面测试类的序列化二进制 //并输出序列化后的xx.TT的信息 DynamicParseFromPBString(pbstr, "xx.TT" , [pb_str](::google::protobuf::Message* msg) { if(msg->ParseFromString(pb_str)) { std::cout << msg->DebugString() << std::endl; } }); return 0; }

测试结果输出

name: "test_name" age: 100 phones: "138xxxxxx" phones: "139xxxxxx" aa { name: "a_name_0" age: 100 } aa { name: "a_name_1" age: 101 } aa { name: "a_name_2" age: 102 } =============================== name: "test_name" aa { name: "a_name_0" age: 100 } aa { name: "a_name_1" age: 101 } aa { name: "a_name_2" age: 102 } 2: 100 3 { 6: 0x7878787878783833 } 3 { 6: 0x7878787878783933 } =============================== name: "test_name" age: 100 phones: "138xxxxxx" phones: "139xxxxxx" aa { name: "a_name_0" age: 100 } aa { name: "a_name_1" age: 101 } aa { name: "a_name_2" age: 102 } =============================== name: "test_name" age: 100 phones: "138xxxxxx" phones: "139xxxxxx" aa { name: "a_name_0" age: 100 } aa { name: "a_name_1" age: 101 } aa { name: "a_name_2" age: 102 }

其他相关

C++ 实用技术 – GOOGLE PROTOBUF反射技术 – 基础API
C++ 实用技术 – GOOGLE PROTOBUF反射技术 – 转JSON格式
C++ 实用技术 – GOOGLE PROTOBUF反射技术 – 转成YAML格式

个人主页