protobuf 反射及应用

protobuf 反射构建 Message

Message* createMessage(const std::string& typeName)
{
  Message* message = NULL;
  const Descriptor* descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
  if (descriptor)
  {
    const Message* prototype = MessageFactory::generated_factory()->GetPrototype(descriptor);
    if (prototype)
    {
      message = prototype->New();
    }
  }
  return message;
}

protobuf 动态加载 proto 文件

protobuf 的 proto 文件都是使用懒加载的,如果需要热加载,可以使用如下代码:

#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>

using namespace std;
using namespace ::google::protobuf;

class ErrorCollector : public compiler::MultiFileErrorCollector {
    virtual void AddError(const std::string &filename, int line, int column, const std::string &message) {
        // define import error collector
        printf("%s, %d, %d, %s\n", filename.c_str(), line, column, message.c_str());
    }
};

	compiler::DiskSourceTree sourceTree; // proto 根目录映射
    std::map<std::string, std::string> mapPathList = { // 运行目录 -> 磁盘目录
            {"", "proto"}
    };
    for (auto &path : mapPathList) {
        std::cout << "map from " << path.first << " to " << path.second << std::endl;
        sourceTree.MapPath(path.first, path.second);
    }
    compiler::Importer importer(&sourceTree, new ErrorCollector());// 加载 proto 文件
    importer.Import(FLAGS_proto_name);
    const DescriptorPool *descriptor_pool = importer.pool();// proto 文件描述符
    const DynamicMessageFactory *message_factory = new google::protobuf::DynamicMessageFactory(importer.pool()); // proto 构造器

protobuf channel 动态调用 service

  1. ::google::protobuf::RpcChannel 使用 ip 和 port 构建 channel
  2. ::google::protobuf::RpcController 接收返回的控制信息
  3. 动态构建 req 和 resp 的 message
  4. 获取 service 的 method 描述符
  5. Closure = nullptr
  virtual void CallMethod(const MethodDescriptor* method,
                          RpcController* controller, const Message* request,
                          Message* response, Closure* done) = 0;

Message 和 Json::Value 互转

using namespace std;
using namespace google::protobuf;
 
using ProtobufMsg = ::google::protobuf::Message;
using ProtobufReflection = ::google::protobuf::Reflection;
using ProtobufFieldDescriptor = ::google::protobuf::FieldDescriptor;
using ProtobufDescriptor = ::google::protobuf::Descriptor;
 
void myMessage2Json(const ProtobufMsg& message, Json::Value& json, bool enum2str = false);
void myRepeatedMessage2Json(const ProtobufMsg& message, const ProtobufFieldDescriptor* field,const ProtobufReflection* reflection, Json::Value& json, bool enum2str);
 
bool myJson2Message(const Json::Value& json, ProtobufMsg& message, bool str2enum = false);
bool myJson2RepeatedMessage(const Json::Value& json, ProtobufMsg& message, const ProtobufFieldDescriptor* field,const ProtobufReflection* reflection, bool str2enum);
 
bool myJson2Message(const Json::Value& json, ProtobufMsg& message, bool str2enum) {
    auto descriptor = message.GetDescriptor();
    auto reflection = message.GetReflection();
    if (nullptr == descriptor || nullptr == reflection) return false;
    //取出字段数并遍历每个字段
    auto count = descriptor->field_count();
    for (auto i = 0; i < count; ++i) {
        const auto field = descriptor->field(i);
        if (nullptr == field) continue;
        if (!json.isMember(field->name())) continue;
        //如果json中有这个字段,就取出来
        auto& value = json[field->name()];
        if (value.isNull()) continue;
        //如果字段是rpt的,而且json中取到的value也是array就调用 Json2RepeatedMessage 逻辑
        if (field->is_repeated()) {
            if (!value.isArray()) {
                return false;
            } else {
                myJson2RepeatedMessage(value, message, field, reflection, str2enum);
                continue;
            }
        }
        //然后就是根据字段类型进行分别处理了
        switch (field->type()) {
            case ProtobufFieldDescriptor::TYPE_BOOL: {
                if (value.isBool())
                    reflection->SetBool(&message, field, value.asBool());
                else if (value.isIntegral())
                    reflection->SetBool(&message, field, value.asBool());
                else if (value.isString()) {
                    if (value.asString() == "true")
                        reflection->SetBool(&message, field, true);
                    else if (value.asString() == "false")
                        reflection->SetBool(&message, field, false);
                }
            } break;
 
            case ProtobufFieldDescriptor::TYPE_ENUM: {
                auto const* pedesc = field->enum_type();
                const ::google::protobuf::EnumValueDescriptor* pevdesc = nullptr;
 
                if (str2enum) {
                    pevdesc = pedesc->FindValueByName(value.asString());
                } else {
                    pevdesc = pedesc->FindValueByNumber(value.asInt());
                }
 
                if (nullptr != pevdesc) {
                    reflection->SetEnum(&message, field, pevdesc);
                }
            } break;
 
            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32: {
                if (value.isInt()) reflection->SetInt32(&message, field, value.asInt());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32: {
                if (value.isInt()) reflection->SetUInt32(&message, field, value.asUInt());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64: {
                if (value.isInt64()) reflection->SetInt64(&message, field, value.asInt64());
            } break;
            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64: {
                if (value.isInt64()) reflection->SetUInt64(&message, field, value.asUInt64());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_FLOAT: {
                if (value.isNumeric()) reflection->SetFloat(&message, field, value.asFloat());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_DOUBLE: {
                if (value.isDouble()) reflection->SetDouble(&message, field, value.asDouble());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES: {
                if (value.isString()) reflection->SetString(&message, field, value.asString());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_MESSAGE: {
                if (value.isObject()) myJson2Message(value, *reflection->MutableMessage(&message, field));
            } break;
 
            default:
                break;
        }
    }
    return true;
}
 
bool myJson2RepeatedMessage(const Json::Value& json, ProtobufMsg& message, const ProtobufFieldDescriptor* field,
                                   const ProtobufReflection* reflection, bool str2enum) {
    int count = json.size();
    //对json数组里面的每个元素进行处理
    for (auto j = 0; j < count; ++j) {
        //根据元素的数据类型进行switch
        switch (field->type()) {
            case ProtobufFieldDescriptor::TYPE_BOOL: {
                if (json[j].isBool())
                    reflection->AddBool(&message, field, json[j].asBool());
                else if (json[j].isUInt())
                    reflection->AddBool(&message, field, json[j].asBool());
                else if (json[j].isString()) {
                    if (json[j] == "true")
                        reflection->AddBool(&message, field, true);
                    else if (json[j] == "false")
                        reflection->AddBool(&message, field, false);
                }
            } break;
 
            case ProtobufFieldDescriptor::TYPE_ENUM: {
                auto const* pedesc = field->enum_type();
                const ::google::protobuf::EnumValueDescriptor* pevdesc = nullptr;
                if (str2enum) {
                    pevdesc = pedesc->FindValueByName(json[j].asString());
 
                } else {
                    pevdesc = pedesc->FindValueByNumber(json[j].asInt());
                }
                if (nullptr != pevdesc) {
                    reflection->AddEnum(&message, field, pevdesc);
                }
 
            } break;
 
            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32: {
                if (json[j].isNumeric()) reflection->AddInt32(&message, field, json[j].asInt());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32: {
                if (json[j].isNumeric()) reflection->AddUInt32(&message, field, json[j].asUInt());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64: {
                if (json[j].isNumeric()) reflection->AddInt64(&message, field, json[j].asInt64());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64: {
                if (json[j].isNumeric()) reflection->AddUInt64(&message, field, json[j].asUInt64());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_FLOAT: {
                if (json[j].isNumeric()) reflection->AddFloat(&message, field, json[j].asFloat());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_DOUBLE: {
                if (json[j].isNumeric()) reflection->AddDouble(&message, field, json[j].asDouble());
            } break;
 
            case ProtobufFieldDescriptor::TYPE_MESSAGE: {
                if (json[j].isObject()) myJson2Message(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;
}
 
void myMessage2Json(const ProtobufMsg& message, Json::Value& json, bool enum2str) {
	//cout << "enter myMessage2Json" << endl;
    auto descriptor = message.GetDescriptor();
    auto reflection = message.GetReflection();
    if (nullptr == descriptor || nullptr == descriptor) return;
 
    auto count = descriptor->field_count();
    //cout << "count is " << count << endl;
    for (auto i = 0; i < count; ++i) {
        const auto field = descriptor->field(i);
        //cout << "field name is " << field->name();
        //cout << ";  filed type is " << field->type();
        //cout << ";  is_repeated " << field->is_repeated() << endl;
 
        if (field->is_repeated()) {
            if (reflection->FieldSize(message, field) > 0)
                myRepeatedMessage2Json(message, field, reflection, json[field->name()], enum2str);
 
            continue;
        }
 
        if (!reflection->HasField(message, field)) {
            continue;
        }
 
        switch (field->type()) {
            case ProtobufFieldDescriptor::TYPE_MESSAGE: {
                const ProtobufMsg& tmp_message = reflection->GetMessage(message, field);
                if (0 != tmp_message.ByteSize()) myMessage2Json(tmp_message, json[field->name()]);
            } break;
 
            case ProtobufFieldDescriptor::TYPE_BOOL:
                json[field->name()] = reflection->GetBool(message, field) ? true : false;
                break;
 
            case ProtobufFieldDescriptor::TYPE_ENUM: {
                auto* enum_value_desc = reflection->GetEnum(message, field);
                if (enum2str) {
                    json[field->name()] = enum_value_desc->name();
                } else {
                    json[field->name()] = enum_value_desc->number();
                }
            } break;
 
            case ProtobufFieldDescriptor::TYPE_INT32:
            case ProtobufFieldDescriptor::TYPE_SINT32:
            case ProtobufFieldDescriptor::TYPE_SFIXED32:
                json[field->name()] = reflection->GetInt32(message, field);
                break;
 
            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32:
                json[field->name()] = reflection->GetUInt32(message, field);
                break;
 
            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64:
                json[field->name()] = (Json::Value::Int64)reflection->GetInt64(message, field);
                break;
 
            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64:
                json[field->name()] = (Json::Value::UInt64)reflection->GetUInt64(message, field);
                break;
 
            case ProtobufFieldDescriptor::TYPE_FLOAT:
                json[field->name()] = reflection->GetFloat(message, field);
                break;
 
            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES:
                json[field->name()] = reflection->GetString(message, field);
                break;
 
            default:
                break;
        }
    }
}
 
void myRepeatedMessage2Json(const ProtobufMsg& message, const ProtobufFieldDescriptor* field,
                                   const ProtobufReflection* reflection, Json::Value& json, bool enum2str) {
    if (nullptr == field || nullptr == reflection) {
        myMessage2Json(message, json);
    }
 
    for (auto 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()) {
                    myMessage2Json(tmp_message, tmp_json);
                }
            } break;
 
            case ProtobufFieldDescriptor::TYPE_BOOL:
                tmp_json = reflection->GetRepeatedBool(message, field, i) ? true : false;
                break;
 
            case ProtobufFieldDescriptor::TYPE_ENUM: {
                auto* 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 = reflection->GetRepeatedInt32(message, field, i);
                break;
 
            case ProtobufFieldDescriptor::TYPE_UINT32:
            case ProtobufFieldDescriptor::TYPE_FIXED32:
                tmp_json = reflection->GetRepeatedUInt32(message, field, i);
                break;
 
            case ProtobufFieldDescriptor::TYPE_INT64:
            case ProtobufFieldDescriptor::TYPE_SINT64:
            case ProtobufFieldDescriptor::TYPE_SFIXED64:
                tmp_json = (Json::Value::Int64)reflection->GetRepeatedInt64(message, field, i);
                break;
 
            case ProtobufFieldDescriptor::TYPE_UINT64:
            case ProtobufFieldDescriptor::TYPE_FIXED64:
                tmp_json = (Json::Value::UInt64)reflection->GetRepeatedUInt64(message, field, i);
                break;
 
            case ProtobufFieldDescriptor::TYPE_FLOAT:
                tmp_json = reflection->GetRepeatedFloat(message, field, i);
                break;
 
            case ProtobufFieldDescriptor::TYPE_STRING:
            case ProtobufFieldDescriptor::TYPE_BYTES:
                tmp_json = reflection->GetRepeatedString(message, field, i);
                break;
 
            default:
                break;
        }
        json.append(tmp_json);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值