C++实用技术 – protobuf动态解析proto
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格式