1. 程式人生 > >c++ json與protobuf 轉換

c++ json與protobuf 轉換

/*
 *   data:  2018-04-14
 *   author: cc509a
 *   email:
 *   protocol buffer and json convert each other
 *   support array ,enum

*/
#ifndef MYLIB_JSON_2_PB
# define MYLIB_JSON_2_PB

#include "json.h"


#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

namespace mylib {

class MylibJson2Pb {

public:

    typedef ::google::protobuf::Message         ProtobufMsg;
    typedef ::google::protobuf::Reflection      ProtobufReflection;
    typedef ::google::protobuf::FieldDescriptor ProtobufFieldDescriptor;
    typedef ::google::protobuf::Descriptor      ProtobufDescriptor;

public:

/* 列舉是字串 enum2str 傳true */
static void PbMsg2JsonStr(const ProtobufMsg& src, std::string& dst, bool enum2str = false) 
{
    Json::Value value;
    PbMsg2Json(src, value, enum2str);
    Json::FastWriter writer;
    dst = writer.write(value);
}

static bool JsonStr2PbMsg(const std::string& src, ProtobufMsg& dst, bool str2enum = false) 
{
    Json::Value value;
    Json::Reader reader(Json::Features::strictMode());
    if (!reader.parse(src, value))
    {
        LOG_ERROR("parse json string is fail,str=%s", src.c_str());
        return false;
    }
    if(true != Json2PbMsg(value, dst, str2enum))
    {
        LOG_ERROR("pb convert error");
        return false;
    }
    return true;
}



static bool Json2PbMsg(const Json::Value& src, ProtobufMsg& dst, bool str2enum = false)
{
    const ProtobufDescriptor* descriptor = dst.GetDescriptor();
    const ProtobufReflection* reflection = dst.GetReflection();
    if (NULL == descriptor || NULL == reflection) return false;

    int32_t count = descriptor->field_count();
    for (int32_t i = 0; i < count; ++i) 
    {
        const ProtobufFieldDescriptor* field = descriptor->field(i);
        if (NULL == field) continue;

        if(!src.isMember(field->name()))
        {
            continue;
        }
        const Json::Value& value = src[field->name()];

        if (field->is_repeated()) 
        {
            if (!value.isArray()) 
            {
                LOG_ERROR("pb error");
                return false;
            } 
            else 
            {
                Json2RepeatedMessage(value, dst, field, reflection, str2enum);
                continue;
            }
        }
        switch (field->type()) 
        {
            case ProtobufFieldDescriptor::TYPE_BOOL: 
            {
                if (value.isBool())
                {
                    reflection->SetBool(&dst, field, value.asBool());
                }

                else if (value.isInt())
                {
                    reflection->SetBool(&dst, field, value.isInt());
                }

                else if (value.isString())
                {
                    if (value.asString() == "true")
                        reflection->SetBool(&dst, field, true);
                    else if (value.asString() == "false")
                        reflection->SetBool(&dst, field, false);
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32: 
            {
                if (value.isInt()) reflection->SetInt32(&dst, field, value.asInt());
                break;
            }

            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32: 
            {
                if (value.isUInt()) reflection->SetUInt32(&dst, field, value.asUInt());
                break;
            }  

            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64: 
            {
                if (value.isInt()) reflection->SetInt64(&dst, field, value.asInt64());
                break;
            } 
            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64: 
            {
                if (value.isUInt()) reflection->SetUInt64(&dst, field, value.asUInt64());
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_FLOAT: 
            {
                if (value.isDouble()) reflection->SetFloat(&dst, field, value.asFloat());
                break;
            }

            case ProtobufFieldDescriptor::TYPE_DOUBLE: 
            {
                if (value.isDouble()) reflection->SetDouble(&dst, field, value.asDouble());
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES: 
            {
                if (value.isString()) reflection->SetString(&dst, field, value.asString());
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_MESSAGE: 
            {
                if (value.isObject()) Json2PbMsg(value, *reflection->MutableMessage(&dst, field));
                break;
            } 
            default:
            {
                break;
            }
        }
    }
    return true;
}

static void PbMsg2Json(const ProtobufMsg& src, Json::Value& dst, bool enum2str = false) 
{
    const ProtobufDescriptor* descriptor = src.GetDescriptor();
    const ProtobufReflection* reflection = src.GetReflection();
    if (NULL == descriptor || NULL == descriptor) return;

    int32_t count = descriptor->field_count();

    for (int32_t i = 0; i < count; ++i) 
    {
        const ProtobufFieldDescriptor* field = descriptor->field(i);
        
        if (field->is_repeated()) 
        {
            if (reflection->FieldSize(src, field) > 0)
                RepeatedMessage2Json(src, field, reflection, dst[field->name()], enum2str);
            continue;
        }


        if (!reflection->HasField(src, field)) 
        {
            continue;
        }

        switch (field->type()) 
        {
            case ProtobufFieldDescriptor::TYPE_MESSAGE: 
            {
                const ProtobufMsg& tmp_message = reflection->GetMessage(src, field);
                if (0 != tmp_message.ByteSize()) PbMsg2Json(tmp_message, dst[field->name()]);
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_BOOL:
                dst[field->name()] = reflection->GetBool(src, field) ? true : false;
                break;

            case ProtobufFieldDescriptor::TYPE_ENUM: 
            {
                const ::google::protobuf::EnumValueDescriptor* enum_value_desc = reflection->GetEnum(src, field);
                if (enum2str) {
                    dst[field->name()] = enum_value_desc->name();
                } else {
                    dst[field->name()] = enum_value_desc->number();
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32:
                dst[field->name()] = Json::Int(reflection->GetInt32(src, field));
                break;

            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32:
                dst[field->name()] = Json::UInt(reflection->GetUInt32(src, field));
                break;

            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64:
                dst[field->name()] = Json::Int64(reflection->GetInt64(src, field));
                break;

            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64:
                dst[field->name()] = Json::UInt64(reflection->GetUInt64(src, field));
                break;

            case ProtobufFieldDescriptor::TYPE_FLOAT:
                dst[field->name()] = reflection->GetFloat(src, field);
                break;

            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES:
                dst[field->name()] = reflection->GetString(src, field);
                break;

            default:
                break;
        }
    }
}



private:
static bool Json2RepeatedMessage(const Json::Value& json, ProtobufMsg& message, 
                                const ProtobufFieldDescriptor* field,
                                const ProtobufReflection* reflection,
                                bool str2enum = false) 
{
    int32_t count = json.size();
    for (int32_t j = 0; j < count; ++j) 
    {
        switch (field->type()) 
        {
            case ProtobufFieldDescriptor::TYPE_BOOL: 
            {
                if (json.isBool())
                {
                    reflection->AddBool(&message, field, json[j].asBool());
                }
                else if (json[j].isInt())
                {
                    reflection->AddBool(&message, field, json[j].asInt());
                }
                else if (json[j].isString()) 
                {
                    if (json[j].asString() == "true")
                    {
                        reflection->AddBool(&message, field, true);
                    }
                    else if (json[j].asString() == "false")
                    {
                        reflection->AddBool(&message, field, false);
                    }
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_ENUM: 
            {
                const ::google::protobuf::EnumDescriptor *pedesc = field->enum_type();
                const ::google::protobuf::EnumValueDescriptor* pevdesc = NULL;
                if (str2enum) 
                {
                    pevdesc = pedesc->FindValueByName(json[j].asString());

                } 
                else 
                {
                    pevdesc = pedesc->FindValueByNumber(json[j].asInt());
                }
                if (NULL != pevdesc) 
                {
                    reflection->AddEnum(&message, field, pevdesc);
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32: 
            {
                if (json[j].isInt()) reflection->AddInt32(&message, field, json[j].asInt());
            } break;

            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32: 
            {
                if (json[j].isUInt()) reflection->AddUInt32(&message, field, json[j].asUInt());
            } break;

            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64: 
            {
                if (json[j].isInt()) reflection->AddInt64(&message, field, json[j].asInt64());
            } break;

            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64: 
            {
                if (json[j].isUInt()) reflection->AddUInt64(&message, field, json[j].asUInt64());
            } break;

            case ProtobufFieldDescriptor::TYPE_FLOAT: 
            {
                if (json[j].isDouble()) reflection->AddFloat(&message, field, json[j].asFloat());
            } break;

            case ProtobufFieldDescriptor::TYPE_DOUBLE: 
            {
                if (json[j].isDouble()) reflection->AddDouble(&message, field, json[j].asDouble());
            } break;

            case ProtobufFieldDescriptor::TYPE_MESSAGE: 
            {
                if (json[j].isObject()) Json2PbMsg(json[j], *reflection->AddMessage(&message, field));
            } break;

            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES: 
            {
                if (json[j].isString()) reflection->AddString(&message, field, json[j].asString());
            } break;

            default:
            {
                break;
            }
        }
    }
    return true;
}



private:

static void RepeatedMessage2Json(const ProtobufMsg& message, 
                                        const ProtobufFieldDescriptor* field,
                                        const ProtobufReflection* reflection, 
                                        Json::Value& json, bool enum2str) 
{
    if (NULL == field || NULL == reflection) 
    {
        PbMsg2Json(message, json);
    }

    for (int32_t i = 0; i < reflection->FieldSize(message, field); ++i) 
    {
        Json::Value tmp_json;
        switch (field->type()) 
        {
            case ProtobufFieldDescriptor::TYPE_MESSAGE: 
            {
                const ProtobufMsg& tmp_message = reflection->GetRepeatedMessage(message, field, i);
                if (0 != tmp_message.ByteSize()) 
                {
                    PbMsg2Json(tmp_message, tmp_json);
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_BOOL:
                tmp_json[field->name()] = reflection->GetRepeatedBool(message, field, i) ? true : false;
                break;

            case ProtobufFieldDescriptor::TYPE_ENUM: 
            {
                const ::google::protobuf::EnumValueDescriptor* enum_value_desc = reflection->GetRepeatedEnum(message, field, i);
                if (enum2str) 
                {
                    tmp_json = enum_value_desc->name();
                } 
                else 
                {
                    tmp_json = enum_value_desc->number();
                }
                break;
            } 

            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32:
                tmp_json[field->name()] = reflection->GetRepeatedInt32(message, field, i);
                break;

            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32:
                tmp_json[field->name()] = reflection->GetRepeatedUInt32(message, field, i);
                break;

            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64:
                tmp_json[field->name()] = (Json::Int64)reflection->GetRepeatedInt64(message, field, i);
                break;

            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64:
                tmp_json[field->name()] = Json::UInt64(reflection->GetRepeatedUInt64(message, field, i));
                break;

            case ProtobufFieldDescriptor::TYPE_FLOAT:
                tmp_json[field->name()] = reflection->GetRepeatedFloat(message, field, i);
                break;

            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES:
                tmp_json[field->name()] = reflection->GetRepeatedString(message, field, i);
                break;

            default:
                break;
        }
        json.append(tmp_json);
    }
}

};


}

#endif