1. Introduction

Asserting that collections of JSON objects are equal can be challenging, especially when the order of elements within the collection isn’t guaranteed. While libraries like Jackson and AssertJ can be used, more specialized tools like JSONassert and hamcrest-json are designed to handle this use case more reliably.

In this tutorial, we’ll explore how to compare collections of JSON objects, focusing on ignoring the order of elements using JSONassert and hamcrest-json.

2. Problem Statement

When we work with collections of JSON objects, the order of elements within a list can vary based on the data source.

Consider the following JSON arrays:

[
  {"id": 1, "name": "Alice", "address": {"city": "NY", "street": "5th Ave"}},
  {"id": 2, "name": "Bob", "address": {"city": "LA", "street": "Sunset Blvd"}}
]
[
  {"id": 2, "name": "Bob", "address": {"city": "LA", "street": "Sunset Blvd"}},
  {"id": 1, "name": "Alice", "address": {"city": "NY", "street": "5th Ave"}}
]

Although these arrays have the same elements, the order differs. A direct string comparison of these arrays will fail due to the order difference, despite their identical data.

Let’s define these JSON arrays as Java variables, then explore how to compare them for equality while ignoring order:

String jsonArray1 = "["
        + "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}}, "
        + "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}"
        + "]";

String jsonArray2 = "["
        + "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}, "
        + "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}}"
        + "]";

3. Using JSONassert for JSON Comparison

JSONassert provides a flexible way to compare JSON data, allowing us to compare JSON objects or arrays as JSON, instead of direct string comparison. Specifically, it can compare arrays while ignoring the order of elements.

With LENIENT mode, JSONAssert focuses purely on content, ignoring the order:

@Test
public void givenJsonArrays_whenUsingJSONAssertIgnoringOrder_thenEqual() throws JSONException {
    JSONAssert.assertEquals(jsonArray1, jsonArray2, JSONCompareMode.LENIENT);
}

In this test, the JSONCompareMode.LENIENT mode allows us to assert equality while ignoring the order of elements. This makes JSONassert ideal for cases where we expect the same data, but the order of elements may vary.

3.1. Ignoring Extra Fields with JSONassert

JSONassert also allows extra fields in JSON objects to be ignored with the same LENIENT mode. This is useful when comparing JSON data where some fields, like metadata or timestamps, aren’t relevant to the test:

@Test
public void givenJsonWithExtraFields_whenIgnoringExtraFields_thenEqual() throws JSONException {
    String jsonWithExtraFields = "["
            + "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}, \"age\": 30}, "
            + "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}, \"age\": 25}"
            + "]";

    JSONAssert.assertEquals(jsonArray1, jsonWithExtraFields, JSONCompareMode.LENIENT);
}

In this example, the test validates that jsonArray1 is equivalent to jsonWithExtraFields, allowing for extra fields like age in the comparison.

4. Using hamcrest-json for JSON Matching

In addition to JSONassert, we can leverage hamcrest-json, a plugin for Hamcrest specifically designed for JSON assertions. This plugin builds on Hamcrest’s matcher functionality and allows us to write expressive and readable assertions when working with JSON in JUnit.

One of the most useful features in hamcrest-json is the allowingAnyArrayOrdering() method. This enables us to compare JSON arrays while ignoring their order:

@Test
public void givenJsonCollection_whenIgnoringOrder_thenEqual() {
    assertThat(jsonArray1, sameJSONAs(jsonArray2).allowingAnyArrayOrdering());
}

This approach ensures that the JSON equality check ignores the order of elements in the array using the sameJSONAs() matcher.

4.1. Ignoring Extra Fields with hamcrest-json

In addition to ignoring array ordering, hamcrest-json provides the allowingExtraUnexpectedFields() utility for handling extra fields. This method enables us to ignore fields present in one JSON object but not the other:

@Test
public void givenJsonWithUnexpectedFields_whenIgnoringUnexpectedFields_thenEqual() {
    String jsonWithUnexpectedFields = "["
            + "{\"id\": 1, \"name\": \"Alice\", \"address\": {\"city\": \"NY\", \"street\": \"5th Ave\"}, \"extraField\": \"ignoreMe\"}, "
            + "{\"id\": 2, \"name\": \"Bob\", \"address\": {\"city\": \"LA\", \"street\": \"Sunset Blvd\"}}"
            + "]";

    assertThat(jsonWithUnexpectedFields, sameJSONAs(jsonArray1).allowingExtraUnexpectedFields());
}

In this example, we validate that jsonWithUnexpectedFields equals jsonArray1, even though it contains an extra field. By combining allowingExtraUnexpectedFields() with allowingAnyArrayOrdering(), we ensure a robust comparison that focuses on matching the data between our JSON arrays.

5. Conclusion

In this article, we’ve demonstrated the ability to compare collections of JSON objects while ignoring the order of elements by using purpose-built libraries like JSONassert and hamcrest-json. These libraries offer a more direct approach over manually parsing JSON into Java objects, providing greater reliability and ease of use.

As always, the complete code samples for this article can be found over on GitHub.