I'm having some trouble using Guava's Maps.difference
Right now, using this code to compare two HashMaps from two different jsons:
//Create maps from the given jsons
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> map1 = gson.fromJson(jsonObject1, type);
Map<String, Object> map2 = gson.fromJson(jsonObject2, type);
//Flatten the maps
Map<String, Object> leftFlatMap = FlatMap.flatten(map1);
Map<String, Object> rightFlatMap = FlatMap.flatten(map2);
//Check differences between both maps
MapDifference<String, Object> difference = Maps.difference(leftFlatMap, rightFlatMap);
Everything works fine, and compares (almost) all the elements correctly. Problem is when one of the elements inside the HashMap is an array of maps and the elements are the same but in a different order. Like this:
FIRST JSON:
{ "body":[
{
"primitive":"VALUE",
"jsonArray":[
{
"element":83284180
},
{
"anotherElement":20832841804
}
]
}
]
}
SECOND JSON:
{
"body":[
{
"primitive":"VALUE",
"jsonArray":[
{
"anotherElement":20832841804
},
{
"element":83284180
}
]
}
]
}
As you can see, element and anotherElement values are the same but as they appear in a different order inside the array, difference shows an error.
Is there any possibility to sort the array before? or any other solution?
Thanks in advance!!
One of possible solutions may be sorting the inner sub array so that it would affect the deserialized maps (however, I think making maps out of JSON objects in this case might be a not very good idea due to deserialization costs and strategies that do not necessarily represent the original JSON object).
Assuming jsonObject1
and jsonObject2
are JsonElement
implementations, you can sort its descendants.
@UtilityClass
public final class JsonElements {
public static List<JsonElement> asListView(final JsonArray jsonArray) {
return new JsonArrayListView(jsonArray);
}
public static void sort(final JsonArray jsonArray, final Comparator<? super JsonElement> comparator) {
Collections.sort(asListView(jsonArray), comparator);
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
private static final class JsonArrayListView
extends AbstractList<JsonElement> {
private final JsonArray jsonArray;
@Override
public JsonElement get(final int index) {
return jsonArray.get(index);
}
@Override
public int size() {
return jsonArray.size();
}
@Override
@SuppressWarnings("MethodDoesntCallSuperMethod")
public JsonElement set(final int index, final JsonElement element) {
return jsonArray.set(index, element);
}
}
}
public final class JsonElementsTest {
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.disableInnerClassSerialization()
.create();
private static final Type stringToObjectMapType = new TypeToken<Map<String, Object>>() {}.getType();
@Test
public void testSort()
throws IOException {
final JsonElement jsonElement1 = ... read the 1st JSON document ...;
final JsonElement jsonElement2 = ... read the 2nd JSON document ...;
final JsonArray jsonSubArray1 = getSubArray(jsonElement1);
final JsonArray jsonSubArray2 = getSubArray(jsonElement2);
Assertions.assertNotEquals(jsonSubArray1, jsonSubArray2);
JsonElements.sort(jsonSubArray1, JsonElementsTest::compare);
JsonElements.sort(jsonSubArray2, JsonElementsTest::compare);
final Map<String, Object> map1 = gson.fromJson(jsonElement1, stringToObjectMapType);
final Map<String, Object> map2 = gson.fromJson(jsonElement2, stringToObjectMapType);
Assertions.assertEquals(map1, map2);
}
private static JsonArray getSubArray(final JsonElement jsonElement) {
return jsonElement.getAsJsonObject()
.get("body")
.getAsJsonArray()
.get(0)
.getAsJsonObject()
.get("jsonArray")
.getAsJsonArray();
}
private static int compare(final JsonElement jsonElement1, final JsonElement jsonElement2)
throws IllegalArgumentException {
final JsonObject jsonObject1 = jsonElement1.getAsJsonObject();
final int size1 = jsonObject1.size();
if ( size1 != 1 ) {
throw new IllegalArgumentException("Size-1 must equal 1, but was " + size1);
}
final JsonObject jsonObject2 = jsonElement2.getAsJsonObject();
final int size2 = jsonObject2.size();
if ( size2 != 1 ) {
throw new IllegalArgumentException("Size-2 must equal 2, but was " + size2);
}
// TODO optimize somehow
final String key1 = jsonObject1.keySet().iterator().next();
final String key2 = jsonObject2.keySet().iterator().next();
return key1.compareTo(key2);
}
}
Also consider sorting the descendants recursively if necessary.
Note that you also might have a mapping for the given JSON documents, but I don't think it's your case, but if it is, then you might want to apply @JsonAdapter
to apply a special ordering deserializer (however I still don't think it's a good idea too). Or else, it is also possible to create a map view for the given JsonObject
s so that it might produce recursive reordering views.