C++ 实用技术 – google protobuf反射技术 – 转JSON格式
C++ 实用技术 – google protobuf反射技术 – 转成JSON格式
方法思路
利用google protobuf的反射技术,实现对任意Message进行遍历,并将Message的各个已知属性和未知的属性,写入到JSON的结构里
精简代码
void serialize_message(const google::protobuf::Message& message, Json::Value& jnode) {
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 Json)
#include <google/protobuf/message.h>
#include <sstream>
#include <iostream>
#include <stdint.h>
#include <json/json.h>
#include "test.pb.h"
void serialize_unknowfieldset(const google::protobuf::UnknownFieldSet& ufs, Json::Value& jnode) {
std::map<int, std::vector<Json::Value> > 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((Json::Int64)uf.varint());
//jnode[std::to_string(uf.number())] = (Json::Int64)uf.varint();
break;
case google::protobuf::UnknownField::TYPE_FIXED32:
kvs[uf.number()].push_back((Json::UInt)uf.fixed32());
//jnode[std::to_string(uf.number())] = (Json::Int)uf.fixed32();
break;
case google::protobuf::UnknownField::TYPE_FIXED64:
kvs[uf.number()].push_back((Json::UInt64)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)) {
Json::Value 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(v);
}
break;
}
}
for(auto& i : kvs) {
if(i.second.size() > 1) {
for(auto& n : i.second) {
jnode[std::to_string(i.first)].append(n);
}
} else {
jnode[std::to_string(i.first)] = i.second[0];
}
}
}
void serialize_message(const google::protobuf::Message& message, Json::Value& jnode) {
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) { \
jnode[field->name()].append((jsontype)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) {
jnode[field->name()].append(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) {
jnode[field->name()].append(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) {
Json::Value vv;
serialize_message(reflection->GetRepeatedMessage(message, field, n), vv);
jnode[field->name()].append(vv);
}
break;
}
}
continue;
}
switch(field->cpp_type()) {
#define XX(cpptype, method, valuetype, jsontype) \
case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: { \
jnode[field->name()] = (jsontype)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: {
jnode[field->name()] = reflection->GetEnum(message, field)->number();
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
jnode[field->name()] = reflection->GetString(message, field);
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
serialize_message(reflection->GetMessage(message, field), jnode[field->name()]);
break;
}
}
}
const auto& ufs = reflection->GetUnknownFields(message);
serialize_unknowfieldset(ufs, jnode);
}
std::string JsonToString(const Json::Value& json) {
Json::FastWriter w;
return w.write(json);
}
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);
}
Json::Value vv;
serialize_message(a, vv);
std::cout << JsonToString(vv) << std::endl;
std::string data;
a.SerializeToString(&data);
test::C c;
c.ParseFromString(data);
Json::Value jnode;
serialize_message(c, jnode);
std::cout << JsonToString(jnode) << std::endl;
std::cout << "DebugString: " << c.DebugString() << std::endl;
return 0;
}
其他相关
C++ Google Protobuf 反射技术基础API
C++ 实用技术 – google protobuf反射技术 – 转成YAML格式