用反射机制合并两个Protobuf

对于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

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值