【问题标题】:Google protocol buffers compareGoogle 协议缓冲区比较
【发布时间】:2010-07-12 11:35:57
【问题描述】:

我想比较Google protocol buffers 的两个消息或(两个子参数)。 我没有找到实现它的 API。

有什么想法吗?

【问题讨论】:

  • 您能具体说明您要比较的内容:消息结构、消息的字段值还是两者兼而有之?

标签: c++ protocol-buffers


【解决方案1】:

您可以为此使用 google::protobuf::util::MessageDifferencer 类。我认为它仅在v3.0.2之后可用:

在 google/protobuf/util 中引入了新的实用函数/类 目录:

  • MessageDifferencer:比较两个原始消息并报告它们的差异。
#include <google/protobuf/util/message_differencer.h>

MessageDifferencer::Equals(msg1, msg2);

【讨论】:

  • 补充一点:MessageDifferencer 仅适用于 C++
  • 根据您的消息类型中的字段是否可以具有默认值,您可能需要考虑使用MessageDifferencer::Equivalent 而不是Equals
  • 确实,我有一个单元测试在比较复杂消息结构的二进制和字符串变体时失败,但 MessageDifferencer 修复了它!
  • 请注意,不幸的是,如果您使用的是精简版库,这将不起作用,因为MessageDifferencer::Equals 要求参数为Message 而不是MessageLite(其中Message导出)。 Equals 需要 GetDescriptor(),这是一个仅在 Message 中实现的功能。因此,在这种情况下,您必须了解其他答案中提到的一种序列化方法,并充分了解其局限性。
【解决方案2】:

您可以依赖这样一个事实,即所有 protobuf 消息都继承自 google::protobuf::MesageLite 类型,这反过来又具有比较任何两个 protobuf 消息所需的一切,无论它们是否属于相同的派生类型:

bool operator==(const google::protobuf::MessageLite& msg_a,
                const google::protobuf::MessageLite& msg_b) {
  return (msg_a.GetTypeName() == msg_b.GetTypeName()) &&
      (msg_a.SerializeAsString() == msg_b.SerializeAsString());
}

编辑

正如下面的 cmets 所指出的,尤其是对于 map 字段,这个答案是不正确的。 map 元素具有不确定的顺序。如果您的消息中可能存在map 字段,请使用MessageDifferencer

【讨论】:

  • 哇。这应该是该用户问题的完美答案!太棒了!
  • 这个方法比MessageDifferencer::Equals(msg1, msg2);
  • 来自他们的文档:> 对于协议缓冲区消息实例 foo,以下检查可能会失败: foo.SerializeAsString() == foo.SerializeAsString() 这是由于编码时字段顺序的不确定性留言:developers.google.com/protocol-buffers/docs/encoding#order
  • 在实践中,我发现上述评论对于map 字段尤其适用。如果您的 protobuf 消息可能包含 map 字段,则 SerializeAsString() 将不确定地失败。
【解决方案3】:

除了使用message.DebugString,你也可以这样做

std::string strMsg;
message.SerializeToString(&strMsg);

同时使用两个消息,然后比较两个(二进制)字符串。我没有测试性能,但我认为它比比较 .DebugString() 返回的人类可读消息字符串要快。 +您可以使用 protobuf-lite 库来做到这一点(而对于 message.DebugString,您需要完整版本)。

【讨论】:

  • 序列化不能保证在二进制文件和时间上保持一致。例如,二进制文件未知的扩展将在已知扩展之后最后序列化(作为未知字段)。所以序列化将取决于序列化时可用的描述符。因此,比较序列化的消息是检查是否相等是不好的。 MessageDifferencer 是这样做的正确方法。
【解决方案4】:

好吧,协议缓冲区只是某些对象类型的序列化格式。为什么不使用协议缓冲区来重构原始对象,然后允许这些对象使用您在类中内置的任何比较逻辑来比较它们自己?

【讨论】:

    【解决方案5】:

    这可能不是理想的解决方案,但我认为可以通过以下方式完成:

    messageA.DebugString() == messageB.DebugString();
    

    除此之外,我认为唯一的解决方案是创建自己的Message 子类并实现bool operator==(const Message&amp;)

    【讨论】:

    • @idimba Tks。但是我想你应该编辑你的问题并提到速度的事情;-)。但是,我想,唯一真正的选择是自己实现比较运算符。
    • @Yousf 正如 Protobuf 文档所说,从 Message 继承并不是要走的路。我同意这一点,因为您必须更改 protoc 生成的文件,因此更改原始 proto 将很困难,因为您必须每次编译都更改它。
    【解决方案6】:

    可以比较描述符的指针(超快):

    if (mMessages[i]->body()->GetDescriptor() == T::descriptor())
    

    mMessages 它是一个带有标头和加密的网络消息池,它创建一个带有 protobuf 正文的数据包(google::protobuf::Message*)。

    所以,为了获得正确类型的消息,我比较了描述符常量指针,这对于每种单一类型的消息都是相同的(不确定,但我没有遇到任何问题)。

    这将是比较 protobuf 消息的最快方法,而不必使用字符串比较,顺便说一下,您可以从描述符中获取类型名称。 :-)

    【讨论】:

    • 喜欢我的回答如何被否决,但这是比较消息的正确方法。
    • 如果您想比较类型,那将是正确的,但这个问题似乎是关于比较内容,这肯定不会。我保证,这就是你投反对票的原因。
    猜你喜欢
    • 1970-01-01
    • 2010-11-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2011-04-23
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多