C++ 实用技术 - google protobuf反射技术 - 转成YAML格式

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

C++ 实用技术 - google protobuf反射技术 - 转成YAML格式

方法思路

利用google protobuf的反射技术,实现对任意Message进行遍历,并将Message的各个已知属性和未知的属性,写入到YAML的结构里

精简代码

void serialize_unknowfieldset(const google::protobuf::UnknownFieldSet& ufs, YAML::Node & ynode){ const google::protobuf::Descriptor* descriptor = message.GetDescriptor(); const google::protobuf::Reflection* reflection = message.GetReflection(); for(int i = 0; i < descriptor->field_count(); ++i) { const google::protobuf::FieldDescriptor* field = descriptor->field(i); ... if(field->is_repeated()) { //解析repeated的字段 ... } else { //解析非repeated的字段 ... } } const auto& ufs = reflection->GetUnknownFields(message); //处理UnknownField字段 ... }

测试的proto文件

package test; enum Type { TYPE_INT = 0; TYPE_FLOAT = 1; } message B { optional string str = 1; repeated int32 i32 = 2; repeated float f = 3; repeated bool b = 4; optional Type t = 5; } message A { optional string name = 1; optional int32 age = 2; optional int32 sex = 3; repeated B bs = 4; } message C { optional string name = 1; }

完整代码(Google Protobuf to YAML)

#include <google/protobuf/message.h> #include <sstream> #include <iostream> #include <stdint.h> #include <yaml-cpp/yaml.h> #include "test.pb.h" void serialize_unknowfieldset(const google::protobuf::UnknownFieldSet& ufs, YAML::Node & ynode) { std::map<int, std::vector<YAML::Node> > kvs; for(int i = 0; i < ufs.field_count(); ++i) { const auto& uf = ufs.field(i); switch(uf.type()) { case google::protobuf::UnknownField::TYPE_VARINT: kvs[uf.number()].push_back(YAML::Node(uf.varint())); //jnode[std::to_string(uf.number())] = (Json::Int64)uf.varint(); break; case google::protobuf::UnknownField::TYPE_FIXED32: kvs[uf.number()].push_back(YAML::Node(uf.fixed32())); //jnode[std::to_string(uf.number())] = (Json::Int)uf.fixed32(); break; case google::protobuf::UnknownField::TYPE_FIXED64: kvs[uf.number()].push_back(YAML::Node(uf.fixed64())); //jnode[std::to_string(uf.number())] = (Json::Int64)uf.fixed64(); break; case google::protobuf::UnknownField::TYPE_LENGTH_DELIMITED: google::protobuf::UnknownFieldSet tmp; auto& v = uf.length_delimited(); if(!v.empty() && tmp.ParseFromString(v)) { YAML::Node vv; serialize_unknowfieldset(tmp, vv); kvs[uf.number()].push_back(vv); //jnode[std::to_string(uf.number())] = vv; } else { //jnode[std::to_string(uf.number())] = v; kvs[uf.number()].push_back(YAML::Node(v)); } break; } } for(auto& i : kvs) { if(i.second.size() > 1) { for(auto& n : i.second) { ynode[std::to_string(i.first)].push_back(n); } } else { ynode[std::to_string(i.first)] = i.second[0]; } } } void serialize_message(const google::protobuf::Message& message, YAML::Node& ynode) { const google::protobuf::Descriptor* descriptor = message.GetDescriptor(); const google::protobuf::Reflection* reflection = message.GetReflection(); for(int i = 0; i < descriptor->field_count(); ++i) { const google::protobuf::FieldDescriptor* field = descriptor->field(i); if(field->is_repeated()) { if(!reflection->FieldSize(message, field)) { continue; } } else { if(!reflection->HasField(message, field)) { continue; } } if(field->is_repeated()) { switch(field->cpp_type()) { #define XX(cpptype, method, valuetype, jsontype) \ case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: { \ int size = reflection->FieldSize(message, field); \ for(int n = 0; n < size; ++n) { \ ynode[field->name()].push_back(reflection->GetRepeated##method(message, field, n)); \ } \ break; \ } XX(INT32, Int32, int32_t, Json::Int); XX(UINT32, UInt32, uint32_t, Json::UInt); XX(FLOAT, Float, float, double); XX(DOUBLE, Double, double, double); XX(BOOL, Bool, bool, bool); XX(INT64, Int64, int64_t, Json::Int64); XX(UINT64, UInt64, uint64_t, Json::UInt64); #undef XX case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { int size = reflection->FieldSize(message, field); for(int n = 0; n < size; ++n) { ynode[field->name()].push_back(reflection->GetRepeatedEnum(message, field, n)->number()); } break; } case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { int size = reflection->FieldSize(message, field); for(int n = 0; n < size; ++n) { ynode[field->name()].push_back(reflection->GetRepeatedString(message, field, n)); } break; } case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { int size = reflection->FieldSize(message, field); for(int n = 0; n < size; ++n) { YAML::Node vv; serialize_message(reflection->GetRepeatedMessage(message, field, n), vv); ynode[field->name()].push_back(vv); } break; } } continue; } switch(field->cpp_type()) { #define XX(cpptype, method, valuetype, jsontype) \ case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: { \ ynode[field->name()] = reflection->Get##method(message, field); \ break; \ } XX(INT32, Int32, int32_t, Json::Int); XX(UINT32, UInt32, uint32_t, Json::UInt); XX(FLOAT, Float, float, double); XX(DOUBLE, Double, double, double); XX(BOOL, Bool, bool, bool); XX(INT64, Int64, int64_t, Json::Int64); XX(UINT64, UInt64, uint64_t, Json::UInt64); #undef XX case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { ynode[field->name()] = reflection->GetEnum(message, field)->number(); break; } case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { ynode[field->name()] = reflection->GetString(message, field); break; } case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { YAML::Node node; serialize_message(reflection->GetMessage(message, field), node); ynode[field->name()] = node; break; } } } const auto& ufs = reflection->GetUnknownFields(message); serialize_unknowfieldset(ufs, ynode); } int main(int argc, char** argv) { test::A a; a.set_name("a\"'name"); a.set_age(10); a.set_sex(5); for(int i =0; i < 5; ++i) { test::B* b = a.add_bs(); b->set_str("str_" + std::to_string(i)); for(int n = 0; n < 3; ++n) { b->add_i32(rand()); b->add_f(rand()); } b->set_t(test::TYPE_INT); } YAML::Node vv; serialize_message(a, vv); std::cout << (vv) << std::endl; std::string data; a.SerializeToString(&data); test::C c; c.ParseFromString(data); YAML::Node ynode; serialize_message(c, ynode); std::cout << ynode << std::endl; std::cout << "DebugString: " << c.DebugString() << std::endl; return 0; }

其他相关

C++ Google Protobuf 反射技术基础API
C++ 实用技术 – google protobuf反射技术 – 转成JSON格式