【问题标题】:Overwriting a repeated field in protobuf MergeFrom vs. MergeFromString覆盖 protobuf MergeFrom 与 MergeFromString 中的重复字段
【发布时间】:2016-11-10 16:22:39
【问题描述】:

假设我有一个原型定义为:

MyProto {
    optional MyWrapper wrapper = 1;
}

地点:

MyWrapper {
    repeated int32 data = 1;
}

当我在 MyProto 的两个文本规范上调用 MergeFromString 时,包装器内重复字段的两个版本被连接起来(一个附加到另一个。)我真的希望它们被覆盖。 MergeFromString 的文档说:

当我们在 |serialized| 中找到一个字段时已经存在于此 留言:

  • 如果它是“重复”字段,我们将追加到列表的末尾。
  • 否则,如果它是一个标量,我们将覆盖我们的字段。
  • 否则,(这是一个不重复的组合),我们递归合并 到现有的复合材料中。

显然,对于包装器,我们谈论的是第三种情况。所以我们递归地合并,在下一次复飞中,我们看到一个重复的字段,并且值被附加到目标。所以我明白为什么会这样了。

将此与MergeFrom 的规范进行比较:

此方法将指定消息的内容合并到 当前消息。在指定消息中设置的奇异字段 覆盖当前消息中的相应字段。重复 附加字段。奇异子消息和组是递归的 合并。

在这种情况下,包装器字段不是单个字段吗,包装器不会被覆盖吗?

所以我的问题是双重的,

1) 这是不一致的,还是我误解了什么?

2) 当我调用MergeFromString 时,如何获得我想要的覆盖而不是合并重复字段的行为?

【问题讨论】:

    标签: c++ protocol-buffers


    【解决方案1】:

    对于您问题的第一部分,您引用的 MergeFrom 规范在技术上是正确的,尽管措辞有些混乱。它说“奇异字段”被覆盖,但“奇异子消息”被递归合并,并且您的包装器将被视为奇异子 消息。

    要获得您想要的行为,您应该能够使用FieldMaskUtil。特别是您可以调用FieldMaskUtil::MergeMessageTo(...) 并传递MergeOptions 配置为替换重复字段而不是连接它们。为此,您首先必须从它们的文本格式表示中解析这两条消息。

    【讨论】:

    • 但是字段掩码实用程序仅适用于消息中存在的重复字段,而不适用于子消息中
    • @faizan 你找到解决办法了吗?
    • 是的。我会写一个关于我如何让它工作的答案。
    【解决方案2】:

    我实施了 StrictMerge 来解决这个问题。解决方案很简单,您只需从目标原型中清除任何不需要的重复字段。但是,只有在看到与源消息中的相应字段冲突时才会清除。

    void StrictMerge(const Message& source, Message* target) {
      ClearRepeatedField(source, target);
      target->MergeFrom(source);
    }
    

    ClearRepeatedField 递归地遍历源和目标。在每个级别,如果源中存在冲突的重复字段,它只会清除目标中的重复字段。

    void ClearRepeatedField(const Message& source, Message* target) {
      const Descriptor* source_descriptor = source.GetDescriptor();
      const Reflection* source_reflection = source.GetReflection();
      const Descriptor* target_descriptor = target->GetDescriptor();
      const Reflection* target_reflection = target->GetReflection();
      for (int i = 0; i < source_descriptor->field_count(); i++) {
        const FieldDescriptor* source_field = source_descriptor->field(I);
        const FieldDescriptor* target_field = target_descriptor->field(I);
        if (source_field->is_map()) {
          // Do nothing for a map
          continue;
        }
        if (source_field->is_repeated()) {
          // Clear only if source field is not empty
          if (source_reflection->FieldSize(source, source_field) > 0) {
            target_reflection->ClearField(target, target_field);
          }
          continue;
        }
        bool has_message_value =
          source_field->type() == FieldDescriptor::TYPE_MESSAGE &&
          source_reflection->HasField(source, source_field);
          if (has_message_value) {
            const Message& source_message = source_reflection->GetMessage(
              source, source_field);
            Message* target_message = target_reflection->MutableMessage(
            target, target_field);
            // Drop repeated fields from this field's target
            ClearRepeatedField(source_message, target_message);
          }
        }
    }
    

    【讨论】:

    • @daltonfury42 如果这有帮助,请告诉我。
    猜你喜欢
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多