39

I want to compare two Messages or (two sub parameters) of Google protocol buffers. I don't find an API to achieve it.

Any ideas?

Antonio
  • 19,451
  • 13
  • 99
  • 197
dimba
  • 26,717
  • 34
  • 141
  • 196
  • Can you specify what exactly you want to compare: message structure, message's field values or the both at once? – abyss.7 Apr 24 '12 at 10:42

6 Answers6

49

You can use the class google::protobuf::util::MessageDifferencer for this. I think it's only available since v3.0.2:

Introduced new utility functions/classes in the google/protobuf/util directory:

  • MessageDifferencer: compare two proto messages and report their differences.
#include <google/protobuf/util/message_differencer.h>

MessageDifferencer::Equals(msg1, msg2);
Community
  • 1
  • 1
Amiga
  • 663
  • 8
  • 13
  • 4
    To add: MessageDifferencer is available for C++ only – deddebme Sep 15 '15 at 19:22
  • 3
    depending on whether fields in your Message types can have default values, you might want to consider `MessageDifferencer::Equivalent` instead of `Equals` – pestophagous Aug 15 '17 at 22:40
  • Indeed, I had a unit test failing on comparing binary and string variants of a complex message structure, but the MessageDifferencer fixes it! – Robert Oct 29 '19 at 09:32
  • 1
    Note that unfortunately this doesn't work if you are using the lite version of the library, as `MessageDifferencer::Equals` requires the arguments to be `Message` and not `MessageLite` (from which `Message` derives). `Equals` requires `GetDescriptor()` which is a feature only implemented in `Message`. So in that case one has to go for one of the serialization methods mentioned in the other answers, well aware of the limitations. – Antonio Aug 16 '21 at 12:59
13

You can rely on the fact that all of your protobuf messages inherit from the google::protobuf::MesageLite type, which in turn has everything you need to compare any two protobuf messages, regardless of if they are even of the same derived type:

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());
}

EDIT

As was pointed out in the comments below, and especially for map fields, this answer is incorrect. map elements have non-deterministic ordering. Use MessageDifferencer if map fields might be present in your messages.

Jake Askeland
  • 253
  • 2
  • 6
  • 1
    Wow. This should be a perfect answer to this user's question! Bravo! – Peaches491 Aug 11 '16 at 23:43
  • 1
    This method is faster than `MessageDifferencer::Equals(msg1, msg2);` – user875367 Jul 18 '18 at 03:29
  • 6
    From their documentation: > The following checks may fail for a protocol buffer message instance foo: foo.SerializeAsString() == foo.SerializeAsString() This is due to non-determinism of field orders when encoding a message: https://developers.google.com/protocol-buffers/docs/encoding#order – pooya13 Aug 26 '19 at 23:04
  • In practice, I've found the above comment to be especially true for `map` fields. If your protobuf messages might have `map` fields, `SerializeAsString()` is going to fail non-deterministically. – Jake Askeland Jun 01 '21 at 17:24
9

Instead of using message.DebugString you could also do

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

with both messages and then compare the two (binary) strings. I didn't test the performance but I assume that it is faster than comparing the human readable message strings returned by .DebugString(). +You can do that with the protobuf-lite library (while for message.DebugString you need the full version).

seanmcl
  • 9,740
  • 3
  • 39
  • 45
Lorenz Winkler
  • 155
  • 1
  • 4
  • 7
    The serialization isn't guaranteed to be consistent across binaries and over time. For example, extensions that are unknown to a binary will be serialized last (as unknown fields), after known extensions. So the serialization will depend on what descriptors are available at the time of serialization. Comparing the serialized messages is therefore a bad was to check for equality. MessageDifferencer is the right way to do this. – Remy Blank Dec 12 '15 at 18:55
1

Well, a protocol buffer is just a serialization format for some object type. Why not use the protocol buffer to reconstruct the original objects, and then allow those objects to compare themselves, using whatever comparison logic you've built into the class?

benjismith
  • 16,559
  • 9
  • 57
  • 80
  • That is my understanding. According to this: https://developers.google.com/protocol-buffers/docs/overview#not-good-fit, "When protocol buffers are serialized, the same data can have many different binary serializations. You cannot compare two messages for equality without fully parsing them." – netskink May 11 '22 at 12:38
-1

This might not be the ideal solution, but I think it could be done by:

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

Other than that, I think the only solution would be to create your own Message child class and implement a bool operator==(const Message&).

Gianni
  • 4,300
  • 18
  • 24
  • @idimba Tks. But then I guess you should edit your question and mention the speed thing ;-). But then, I guess, the only real option would be to implement the comparison operator yourself. – Gianni Jul 12 '10 at 18:41
  • 3
    @Yousf It is not, as Protobuf docs say, that inheriting from Message is not the way to go. I agree with that, as you will have to change file generated by protoc, so changing original proto will be difficult, as you will have to change it **every** compilation. – Alexander Shishenko Sep 18 '14 at 18:52
-6

You can compare the descriptor's pointer (super fast):

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

mMessages it's a pool of network messages with header and crypto which creates a packet with the protobuf body(google::protobuf::Message*).

so, to get the right kind of message i compare the descriptors constant pointer which is the same for every single type of message (not %100 sure but i haven't got any problem so far).

That would be the fastest way to compare a protobuf Message wthout having to use string comparasion, which by the way you gan get the type name from the descriptor. :-)

KukoBits
  • 337
  • 2
  • 5
  • Love how my answer got voted down, but it is the right way to comparare messages. – KukoBits Sep 25 '13 at 23:49
  • 13
    That would be correct if you wanted to compare the types, but this question would seem to be about comparing the contents, which this most certainly does not do. This is why you got the downvote, I'd warrant. – Dark Falcon Oct 22 '13 at 18:42