Testing Two Json Objects for Equality Ignoring Child Order in Java

Testing two JSON objects for equality ignoring child order in Java

As a general architectural point, I usually advise against letting dependencies on a particular serialization format bleed out beyond your storage/networking layer; thus, I'd first recommend that you consider testing equality between your own application objects rather than their JSON manifestations.

Having said that, I'm currently a big fan of Jackson which my quick read of their ObjectNode.equals() implementation suggests does the set membership comparison that you want:

public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != getClass()) {
return false;
}
ObjectNode other = (ObjectNode) o;
if (other.size() != size()) {
return false;
}
if (_children != null) {
for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
String key = en.getKey();
JsonNode value = en.getValue();

JsonNode otherValue = other.get(key);

if (otherValue == null || !otherValue.equals(value)) {
return false;
}
}
}
return true;
}

Ignore specific nodes/attributes while comparing two JSONs

You can use Customization for this. For example, if you need to ignore a top-level attribute named "timestamp" use:

JSONAssert.assertEquals(expectedResponseBody, responseBody,
new CustomComparator(JSONCompareMode.LENIENT,
new Customization("timestamp", (o1, o2) -> true)));

It's also possible to use path expressions like "entry.id". In your Customization you can use whatever method you like to compare the two values. The example above always returns true, no matter what the expected value and the actual value are. You could do more complicated stuff there if you need to.

It is perfectly fine to ignore that values of multiple attributes, for example:

@Test
public void ignoringMultipleAttributesWorks() throws JSONException {
String expected = "{\"timestamp\":1234567, \"a\":5, \"b\":3 }";
String actual = "{\"timestamp\":987654, \"a\":1, \"b\":3 }";

JSONAssert.assertEquals(expected, actual,
new CustomComparator(JSONCompareMode.LENIENT,
new Customization("timestamp", (o1, o2) -> true),
new Customization("a", (o1, o2) -> true)
));
}

There is one caveat when using Customizations: The attribute whose value is to be compared in a custom way has to be present in the actual JSON. If you want the comparison to succeed even if the attribute is not present at all you would have to override CustomComparator for example like this:

@Test
public void extendingCustomComparatorToAllowToCompletelyIgnoreCertainAttributes() throws JSONException {
// AttributeIgnoringComparator completely ignores some of the expected attributes
class AttributeIgnoringComparator extends CustomComparator{
private final Set<String> attributesToIgnore;

private AttributeIgnoringComparator(JSONCompareMode mode, Set<String> attributesToIgnore, Customization... customizations) {
super(mode, customizations);
this.attributesToIgnore = attributesToIgnore;
}

protected void checkJsonObjectKeysExpectedInActual(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException {
Set<String> expectedKeys = getKeys(expected);
expectedKeys.removeAll(attributesToIgnore);
for (String key : expectedKeys) {
Object expectedValue = expected.get(key);
if (actual.has(key)) {
Object actualValue = actual.get(key);
compareValues(qualify(prefix, key), expectedValue, actualValue, result);
} else {
result.missing(prefix, key);
}
}
}
}

String expected = "{\"timestamp\":1234567, \"a\":5}";
String actual = "{\"a\":5}";

JSONAssert.assertEquals(expected, actual,
new AttributeIgnoringComparator(JSONCompareMode.LENIENT,
new HashSet<>(Arrays.asList("timestamp")))
);
}

(With this approach you still could use Customizations to compare other attributes' values in the way you want.)

How to compare two JSON strings when the order of entries keep changing

Jackson Json parser has a nice feature that it can parse a Json String into a Map. You can then query the entries or simply ask on equality:

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.*;

public class Test
{
public static void main(String... args)
{
String input1 = "{\"state\":1,\"cmd\":1}";
String input2 = "{\"cmd\":1,\"state\":1}";
ObjectMapper om = new ObjectMapper();
try {
Map<String, Object> m1 = (Map<String, Object>)(om.readValue(input1, Map.class));
Map<String, Object> m2 = (Map<String, Object>)(om.readValue(input2, Map.class));
System.out.println(m1);
System.out.println(m2);
System.out.println(m1.equals(m2));
} catch (Exception e) {
e.printStackTrace();
}
}
}

The output is

{state=1, cmd=1}
{cmd=1, state=1}
true

How to ignore certain attributes when comparing two json files using skyscreamer

we can use the CustomComparator and write an own custom comparator based on needs

 org.skyscreamer.jsonassert.Customization
org.skyscreamer.jsonassert.comparator.CustomComparator

example:

compareJSON(expected, actual,  new CustomComparator(JSONCompareMode,new Customization(addressList.street,(o1, o2) -> true)))


Related Topics



Leave a reply



Submit