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
- ::google::protobuf::RpcChannel 使用 ip 和 port 构建 channel
- ::google::protobuf::RpcController 接收返回的控制信息
- 动态构建 req 和 resp 的 message
- 获取 service 的 method 描述符
- 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);
}
}