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格式

打赏 赞(7)
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

发表评论

邮箱地址不会被公开。 必填项已用*标注