Reputation: 11
I have a heavily nested json which has to be compared. Comparison referring to structural(fields) being same and values are equal. I also want to print out the differences in the value and structure. Is there a java library that can help this out of the box?
Upvotes: 1
Views: 3305
Reputation: 31
I was also dealing with the same issue, in my case, the JsonObject was deeply nested, was having nested object having arrays having nested objects, and so on. I tried to find several libraries, but none of the libraries offer to compare nested json arrays, as the internal order might be differing & the element of json array can also json object or json array & so on. So following is the generic routine that dealt with my problem, and, this is pretty accurate & have tested it on a huge volume of data. Hope you find it helpful:
public static boolean compare(@NotNull JsonElement jsonElement1, @NotNull JsonElement jsonElement2) {
boolean toBeReturned;
if (jsonElement1.isJsonArray() && jsonElement2.isJsonArray()) {
JsonArray elements1 = jsonElement1.getAsJsonArray();
JsonArray elements2 = jsonElement2.getAsJsonArray();
if (elements1.size() != elements2.size()) {
toBeReturned = false;
} else {
JsonArray removedElements = new JsonArray();
toBeReturned = true;
for (int i = 0; i < elements1.size(); i++) {
JsonElement jsonElement = elements1.get(i);
boolean found = false;
for (int j = 0; j < elements2.size(); j++) {
if (compare(jsonElement, elements2.get(j))) {
found = true;
removedElements.add(elements2.remove(j));
break;
}
}
if (!found) {
toBeReturned = false;
break;
}
}
for (int i = 0; i < removedElements.size(); i++) {
elements2.add(removedElements.get(i));
}
}
} else if (jsonElement1.isJsonNull() && jsonElement2.isJsonNull()) {
toBeReturned = true;
} else if (jsonElement1.isJsonPrimitive() && jsonElement2.isJsonPrimitive()) {
toBeReturned = jsonElement1.equals(jsonElement2);
} else if (jsonElement1.isJsonObject() && jsonElement2.isJsonObject()) {
Set<Map.Entry<String, JsonElement>> map = jsonElement1.getAsJsonObject().entrySet();
JsonObject json2 = jsonElement2.getAsJsonObject();
toBeReturned = true;
for (Map.Entry<String, JsonElement> entry : map) {
if (!json2.has(entry.getKey()) || !compare(entry.getValue(), json2.get(entry.getKey()))) {
toBeReturned = false;
break;
}
}
} else {
toBeReturned = false;
}
return toBeReturned;
}
library is com.google.gson.
to covert a String to JsonElement, you can use following:
JsonParser.parseString(jsonString)
Upvotes: 1
Reputation: 3453
Implementation for Gson library:
public static void compareJson(JsonElement json, JsonElement other) {
compareJson(json, other, "");
}
private static void compareJson(JsonElement json, JsonElement other, String path) {
if (json.equals(other)) return;
if (json.isJsonArray() && other.isJsonArray()) {
JsonArray arrJ = (JsonArray) json;
JsonArray arrO = (JsonArray) other;
int size = Math.min(arrJ.size(), arrO.size());
for (int i = 0; i < size; i++) {
compareJson(arrJ.get(i), arrO.get(i), path + "/" + i);
}
if (arrJ.size() > arrO.size()) {
for (int i = arrO.size(); i < arrJ.size(); i++) {
System.out.println(path + "/ deleted array value: " + arrJ.get(i));
}
} else if (arrJ.size() < arrO.size()) {
for (int i = arrJ.size(); i < arrO.size(); i++) {
System.out.println(path + "/ added array value: " + arrO.get(i));
}
}
} else if (json.isJsonObject() && other.isJsonObject()) {
JsonObject objJ = (JsonObject) json;
JsonObject objO = (JsonObject) other;
for (Entry<String, JsonElement> entry : objJ.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (objO.has(key)) {
compareJson(value, objO.get(key), path + "/" + key);
} else {
System.out.println(path + "/ deleted object entry: \"" + key + "\": " + value);
}
}
for (Entry<String, JsonElement> entry : objO.entrySet()) {
String key = entry.getKey();
JsonElement value = entry.getValue();
if (!objJ.has(key)) {
System.out.println(path + "/ added object entry: \"" + key + "\": " + value);
}
}
} else if (json.isJsonPrimitive() && other.isJsonPrimitive()) {
System.out.println(path + "/ value changed: " + json + " -> " + other);
} else {
System.out.println(path + "/ element changed: " + json + " -> " + other);
}
}
Use:
JsonParser parser = new JsonParser();
compareJson(parser.parse(json1), parser.parse(json2));
Example output:
/glossary/title/ value changed: "example glossary" -> "Example Glossary"
/glossary/GlossDiv/GlossList/GlossEntry/SortAs/ element changed: "SGML" -> null
/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/0/ value changed: "GML" -> "XML"
/glossary/GlossDiv/GlossList/GlossEntry/GlossDef/GlossSeeAlso/ deleted array value: "XML"
/glossary/GlossDiv/GlossList/GlossEntry/ deleted object entry: "GlossSee": "markup"
/glossary/GlossDiv/GlossList/GlossEntry/ added object entry: "GlossType": "markup"
Upvotes: 2
Reputation: 11474
I wrote something similar to this for a video game. Here's the code I used. It leverages the Json library inside of Ox
private Json computeDiff(Json a, Json b) {
Json ret = Json.object();
for (String key : b) {
if (a.hasKey(key)) {
Object aVal = a.getObject(key);
Object bVal = b.getObject(key);
if (aVal instanceof Json && ((Json) aVal).isObject()) {
Json innerJson = computeDiff((Json) aVal, (Json) bVal);
if (innerJson.size() > 0) {
ret.with(key, innerJson);
}
} else {
if (aVal.equals(bVal)) {
continue;
}
ret.with(key, b.getObject(key));
}
} else {
ret.with(key, b.getObject(key));
}
}
for (String key : a) {
if (!b.hasKey(key)) {
// this element got deleted
ret.with(key, "<DELETED>");
}
}
return ret;
}
Upvotes: 0