C++ 实用技术 - google protobuf反射技术 - 转成JSON格式
方法思路精简代码测试的proto文件完整代码(Google Protobuf to 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()) {
...
} else {
...
}
}
const auto& ufs
= reflection
->GetUnknownFields(message
);
...
}
测试的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());
break;
case google
::protobuf
::UnknownField
::TYPE_FIXED32
:
kvs
[uf
.number()].push_back((Json
::UInt
)uf
.fixed32());
break;
case google
::protobuf
::UnknownField
::TYPE_FIXED64
:
kvs
[uf
.number()].push_back((Json
::UInt64
)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
);
} else {
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格式