Add MessageDifferencer::set_unpack_any to allow disabling Any unpacking.

PiperOrigin-RevId: 1018289831
diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc
index 8b8a1d5..27cd1e1 100644
--- a/src/google/protobuf/util/message_differencer.cc
+++ b/src/google/protobuf/util/message_differencer.cc
@@ -609,7 +609,8 @@
                                  const Message& message2, int unpacked_any,
                                  std::vector<SpecificField>* parent_fields) {
   // Expand google.protobuf.Any payload if possible.
-  if (message1.GetDescriptor()->full_name() == internal::kAnyFullTypeName) {
+  if (unpack_any_ &&
+      message1.GetDescriptor()->full_name() == internal::kAnyFullTypeName) {
     std::unique_ptr<Message> data1;
     std::unique_ptr<Message> data2;
     if (unpack_any_field_.UnpackAny(message1, &data1) &&
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index d3f0b80..d3e21b4 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -88,6 +88,7 @@
 // Note on handling of google.protobuf.Any: MessageDifferencer automatically
 // unpacks Any::value into a Message and compares its individual fields.
 // Messages encoded in a repeated Any cannot be compared using TreatAsMap.
+// This can be disabled with set_unpack_any(false).
 //
 // Note on thread-safety: MessageDifferencer is *not* thread-safe. You need to
 // guard it with a lock to use the same MessageDifferencer instance from
@@ -564,6 +565,13 @@
     report_ignores_ = report_ignores;
   }
 
+  // Tells the differencer whether or not to unpack google.protobuf.Any
+  // messages and compare their payloads field by field. When false, Any
+  // messages are compared as regular messages (i.e. by their raw type_url and
+  // value bytes). This method must be called before Compare. The default for a
+  // new differencer is true.
+  void set_unpack_any(bool unpack_any) { unpack_any_ = unpack_any; }
+
   // Sets the scope of the comparison (as defined in the Scope enumeration
   // above) that is used by this differencer when determining which fields to
   // compare between the messages.
@@ -967,6 +975,7 @@
   bool report_moves_;
   bool report_ignores_;
   bool force_compare_no_presence_ = false;
+  bool unpack_any_ = true;
 
   std::string* output_string_;
 
diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc
index 3be1e3c..b387eec 100644
--- a/src/google/protobuf/util/message_differencer_unittest.cc
+++ b/src/google/protobuf/util/message_differencer_unittest.cc
@@ -60,6 +60,7 @@
 namespace {
 
 using ::google::protobuf::internal::UnsetFieldsMetadataMessageDifferencerTestUtil;
+using ::testing::HasSubstr;
 
 
 proto3_unittest::TestNoPresenceField MakeTestNoPresenceField() {
@@ -4300,6 +4301,45 @@
           R"(type_url: ".+/proto2_unittest.TestField\" -> ".+/proto2_unittest.ForeignMessage")"));
 }
 
+TEST(AnyTest, UnpackAnyDisabled) {
+  proto2_unittest::TestField value1, value2;
+  value1.set_a(20);
+  value2.set_a(21);
+
+  proto2_unittest::TestAny m1, m2;
+  ABSL_CHECK(m1.mutable_any_value()->PackFrom(value1));
+  ABSL_CHECK(m2.mutable_any_value()->PackFrom(value2));
+  util::MessageDifferencer message_differencer;
+  message_differencer.set_unpack_any(false);
+  std::string difference_string;
+  message_differencer.ReportDifferencesToString(&difference_string);
+  EXPECT_FALSE(message_differencer.Compare(m1, m2));
+  // The Any is compared as a regular message, so the diff is on the raw value.
+  EXPECT_THAT(difference_string, HasSubstr("modified: any_value.value: "));
+}
+
+TEST(AnyTest, UnpackAnyDisabledComparesRawBytes) {
+  // Two different encodings of the same logical payload: equal when unpacked,
+  // but not equal when compared by raw bytes.
+  proto2_unittest::TestAny m1, m2;
+  m1.mutable_any_value()->set_type_url(
+      "type.googleapis.com/proto2_unittest.TestField");
+  m1.mutable_any_value()->set_value(absl::string_view("\x08\x01", 2));
+  m2.mutable_any_value()->set_type_url(
+      "type.googleapis.com/proto2_unittest.TestField");
+  m2.mutable_any_value()->set_value(absl::string_view("\x08\x00\x08\x01", 4));
+
+  util::MessageDifferencer unpacking_differencer;
+  EXPECT_TRUE(unpacking_differencer.Compare(m1, m2));
+
+  util::MessageDifferencer raw_differencer;
+  raw_differencer.set_unpack_any(false);
+  EXPECT_FALSE(raw_differencer.Compare(m1, m2));
+
+  m2.mutable_any_value()->set_value(m1.any_value().value());
+  EXPECT_TRUE(raw_differencer.Compare(m1, m2));
+}
+
 TEST(Anytest, TreatAsSet) {
   proto2_unittest::TestField value1, value2;
   value1.set_a(20);