对于protobuf的合并操作,官方api提供了MergeFrom函数。如:
pd1.MergeFrom(pb2),
该方法对于非repeated类型,优先使用pb2,对于repeated类型,就是将两个字段的值取并集,并且无法去重。有时无法满足我们的项目需求。
我们可以使用protobuf的google::protobuf::Descriptor和google::protobuf::Reflection实现将两个相同类型的pb合并。代码如下:
addressbook.proto文件:
package zhb;
message Person {
required string name = 1;
required int32 age = 2;
}
message Model {
optional int32 age = 1;
optional string name = 2;
repeated int32 gender = 3;
repeated Person person = 4;
}
合并函数:
#include <iostream>
#include <string>
#include "addressbook.pb.h"
#define CASE_FIELD_TYPE(cpptype, method, valuetype) \
case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: { \
const valuetype& value = src_reflection->Get##method(src, field); \
dst_reflection->Set##method(dst, field, value); \
break; \
}
#define CASE_REPEATED_FIELD_TYPE(cpptype, method, valuetype) \
case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: { \
const valuetype& value = src_reflection->GetRepeated##method(src, field, j); \
dst_reflection->Add##method(dst, field, value); \
break; \
}
using namespace std;
template <typename T>
void MergeProto(const T& src, T* dst) {
const google::protobuf::Descriptor* descriptor = dst->GetDescriptor();
const google::protobuf::Reflection* src_reflection = src.GetReflection();
const google::protobuf::Reflection* dst_reflection = dst->GetReflection();
int field_count = descriptor->field_count();
for (int i = 0; i < field_count; ++i) {
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
const std::string field_name = field->name();
if (!field->is_repeated()) {
const bool src_has_field = src_reflection->HasField(src, field);
const bool dst_has_field = dst_reflection->HasField(*dst, field);
if (src_has_field && !dst_has_field) {
switch (field->cpp_type()) {
CASE_FIELD_TYPE(INT32, Int32, int);
CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
CASE_FIELD_TYPE(FLOAT, Float, float);
CASE_FIELD_TYPE(DOUBLE, Double, double);
CASE_FIELD_TYPE(BOOL, Bool, bool);
CASE_FIELD_TYPE(INT64, Int64, int64_t);
CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
CASE_FIELD_TYPE(STRING, String, std::string);
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
const int value = src_reflection->GetEnum(src, field)->number();
dst_reflection->AddEnumValue(dst, field, value);
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
const google::protobuf::Message& src_message = src_reflection->GetMessage(src, field);
dst_reflection->MutableMessage(dst, field)->CopyFrom(src_message);
break;
}
}
}
} else {
const int src_field_size = src_reflection->FieldSize(src, field);
const int dst_field_size = dst_reflection->FieldSize(*dst, field);
if (src_field_size > 0 && dst_field_size == 0) {
for (int j = 0; j < src_field_size; ++j) {
switch (field->cpp_type()) {
CASE_REPEATED_FIELD_TYPE(INT32, Int32, int);
CASE_REPEATED_FIELD_TYPE(UINT32, UInt32, uint32_t);
CASE_REPEATED_FIELD_TYPE(FLOAT, Float, float);
CASE_REPEATED_FIELD_TYPE(DOUBLE, Double, double);
CASE_REPEATED_FIELD_TYPE(BOOL, Bool, bool);
CASE_REPEATED_FIELD_TYPE(INT64, Int64, int64_t);
CASE_REPEATED_FIELD_TYPE(UINT64, UInt64, uint64_t);
CASE_REPEATED_FIELD_TYPE(STRING, String, std::string);
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
const int value = src_reflection->GetRepeatedEnum(src, field, j)->number();
dst_reflection->AddEnumValue(dst, field, value);
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
const google::protobuf::Message& src_message = src_reflection->GetRepeatedMessage(src, field, j);
dst_reflection->AddMessage(dst, field)->CopyFrom(src_message);
break;
}
}
}
}
}
}
}
void MergeMessages() {
zhb::Model model1;
model1.set_age(10);
model1.set_name("zhb1");
model1.add_gender(1);
model1.add_gender(10);
zhb::Person* person = model1.add_person();
person->set_name("per");
person->set_age(10);
zhb::Model model2;
model2.set_age(1);
model2.set_name("zhb2");
model2.add_gender(2);
model2.add_gender(10);
model1.MergeFrom(model2);
cout << "model1:\n" << model1.DebugString() << endl;
MergeProto(model1, &model2);
cout << "model2:\n" << model2.DebugString() << endl;
}
int main(int argc, char **argv) {
MergeMessages();
return 0;
}
运行结果:
model1:
age: 1
name: "zhb2"
gender: 1
gender: 10
gender: 2
gender: 10
person {
name: "per"
age: 10
}
model2:
age: 1
name: "zhb2"
gender: 2
gender: 10
person {
name: "per"
age: 10
}
从运行结果来看:
model1.MergeFrom(model2);函数的作用:
非repeated类型优先使用model2,repeated类型合并操作,无法去重
MergeProto(model1, &model2);函数的作用:
非repeated类型优先使用model2,repeated类型优先使用model2,如果model2有,就忽略model1
参考api:
protobuf反射官方api