JSON-P provides a nice JSON API for Java applications. However, as JSON is, the objects are ordered (as well as arrays). However, sometimes, you want to compare two objects but ignoring all potential ordering.

One use case is to compare two OpenAPI JSON dump, in this case you don't really care to know if both represent the same API, if the endpoint A is before the endpoint B or the opposite.

To solve that we can implement a simple method visiting the JsonObject structure and ignoring the order (note that this implementation doesn't respect the visitor pattern but this is not really needed for something that simple):

public static boolean areEqualsIgnoringOrder(final JsonValue oldValue, final JsonValue newValue) {
    if (!oldValue.getValueType().equals(newValue.getValueType())) {
        return false;
    }
    switch (oldValue.getValueType()) {
    case STRING:
        return JsonString.class.cast(oldValue).getString().equals(JsonString.class.cast(newValue).getString());
    case NUMBER:
        return JsonNumber.class.cast(oldValue).doubleValue() == JsonNumber.class.cast(newValue).doubleValue();
    case OBJECT:
        final JsonObject oldObject = oldValue.asJsonObject();
        final JsonObject newObject = newValue.asJsonObject();
        if (!oldObject.keySet().equals(newObject.keySet())) {
            return false;
        }
        return oldObject
                .keySet()
                .stream()
                .map(key -> areEqualsIgnoringOrder(oldObject.get(key), newObject.get(key)))
                .reduce(true, (a, b) -> a && b);
    case ARRAY:
        final JsonArray oldArray = oldValue.asJsonArray();
        final JsonArray newArray = newValue.asJsonArray();
        if (oldArray.size() != newArray.size()) {
            return false;
        }
        if (oldArray.isEmpty()) {
            return true;
        }
        final Iterator<JsonValue> oldIt = oldArray.iterator();
        final Iterator<JsonValue> newIt = newArray.iterator();
        while (oldIt.hasNext()) {
            final JsonValue oldItem = oldIt.next();
            final JsonValue newItem = newIt.next();
            if (!areEqualsIgnoringOrder(oldItem, newItem)) {
                return false;
            }
        }
        return true;
    default:
        // value type check was enough
        return true;
    }
}

The overall idea is to compare "normally" the leaves (string, number, boolean and null values) and specifically the objects and arrays. In this implementation, the array order is preserved but iterating over one of the two array to check if one item is equal to current one of the other list would remove the array order as well (snippet after these explanations). The object comparison starts by ensuring the keys of both objects are the same then it recompares both values with the same logic. At the end the objects were compared ignoring the order, using the HashMap side of the LinkedHashMap under the JsonObject.

If you want to also ignore the array ordering you can replace the array comparison by this code:

case ARRAY:
    final JsonArray oldArray = oldValue.asJsonArray();
    final JsonArray newArray = newValue.asJsonArray();
    if (oldArray.size() != newArray.size()) {
        return false;
    }
    if (oldArray.isEmpty()) {
        return true;
    }
    for (final JsonValue oldItem : oldArray) {
        if (newArray.stream().noneMatch(newitem -> areEqualsIgnoringOrder(oldItem, newitem))) {
            return false;
        }
    }
    return true;

 

From the same author:

In the same category: