Last active
November 1, 2024 11:55
-
-
Save dmarcosl/28f3691eb26c4ece73163664cef8a2b6 to your computer and use it in GitHub Desktop.
Comparing 2 unordered jsons with Jackson
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.core.JsonProcessingException; | |
import com.fasterxml.jackson.databind.JsonNode; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import com.exceptions.JsonHandleException; | |
import java.util.Iterator; | |
import java.util.Map; | |
public class JsonUtils { | |
private static final ObjectMapper mapper = new ObjectMapper(); | |
protected static JsonNode readJson(String json) throws JsonHandleException { | |
try { | |
return mapper.readTree(json); | |
} catch (JsonProcessingException e) { | |
throw new JsonHandleException(String.format("Error parsing json to JsonNode: %s", json), e); | |
} | |
} | |
public static boolean compareTwoJsons(String jsonStr1, String jsonStr2) | |
throws JsonHandleException { | |
JsonNode json1 = readJson(jsonStr1); | |
JsonNode json2 = readJson(jsonStr2); | |
return compareTwoJsonNodes(json1, json2); | |
} | |
private static boolean compareTwoJsonNodes(JsonNode json1, JsonNode json2) { | |
// Number of fields (object) / elements (array) | |
if (json1.size() != json2.size()) { | |
return false; | |
} | |
for (Iterator<Map.Entry<String, JsonNode>> it = json1.fields(); it.hasNext(); ) { | |
Map.Entry<String, JsonNode> entry = it.next(); | |
String key = entry.getKey(); | |
JsonNode value1 = entry.getValue(); | |
// Key exists in both | |
if (!json2.has(key)) { | |
return false; | |
} | |
JsonNode value2 = json2.get(key); | |
// If one is null and the other is not | |
if ((value1.isNull() || value2.isNull()) && value1.equals(value2)) { | |
return false; | |
} | |
// Length (object) / size (array) | |
if (value1.size() != value2.size()) { | |
return false; | |
} | |
if (value1.isArray()) { | |
// Compare each column of each row of both arrays to find a match regardless | |
// of the order of the row or column | |
for (int i = 0; i < value1.size(); i++) { | |
for (int j = 0; j < value2.size(); j++) { | |
if (compareTwoJsonNodes(value1.get(i), value2.get(j))) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} else { | |
// Compare the class and the value | |
if (!value1.getClass().equals(value2.getClass())) { | |
return false; | |
} else if (!value1.equals(value2)) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.junit.jupiter.api.Assertions.*; | |
import com.exceptions.JsonHandleException; | |
import org.junit.jupiter.api.Test; | |
import org.junit.jupiter.api.extension.ExtendWith; | |
import org.springframework.test.context.junit.jupiter.SpringExtension; | |
@ExtendWith(SpringExtension.class) | |
class JsonUtilsTest { | |
@Test | |
void compareTwoJsons_depth1_same__true() { | |
var jsonStr1 = "{\"key1\": \"value1\", \"key2\": 123, \"key3\": true}"; | |
var jsonStr2 = "{\"key1\": \"value1\", \"key2\": 123, \"key3\": true}"; | |
assertTrue(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_same_unordered__true() { | |
var jsonStr1 = "{\"key1\": \"value1\", \"key2\": 123, \"key3\": true}"; | |
var jsonStr2 = "{\"key3\": true, \"key1\": \"value1\", \"key2\": 123}"; | |
assertTrue(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_different_keys__false() { | |
var jsonStr1 = "{\"key1\": \"value1\", \"key2\": 123, \"key3\": true}"; | |
var jsonStr2 = "{\"key4\": \"value1\", \"key5\": 123, \"key6\": true}"; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_different_size__false() { | |
var jsonStr1 = "{\"key1\": \"value1\", \"key2\": 123, \"key3\": true}"; | |
var jsonStr2 = "{\"key1\": \"value1\", \"key2\": 123}"; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_nulls__false() { | |
var jsonStr1 = "{\"key1\": null, \"key2\": null, \"key3\": null}"; | |
var jsonStr2 = "{\"key1\": null, \"key2\": 123, \"key3\": null}"; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_different_class__false() { | |
var jsonStr1 = "{\"key1\": \"value1\"}"; | |
var jsonStr2 = "{\"key1\": false}"; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth1_different_value__false() { | |
var jsonStr1 = "{\"key1\": \"value1\"}"; | |
var jsonStr2 = "{\"key1\": \"valueDifferent\"}"; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth2_same__true() { | |
var jsonStr1 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key1", "value": "value", "type": "String" }, | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key3", "value": 1, "type": "Integer" } | |
] | |
} | |
"""; | |
var jsonStr2 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key1", "value": "value", "type": "String" }, | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key3", "value": 1, "type": "Integer" } | |
] | |
} | |
"""; | |
assertTrue(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth2_same_unordered__true() { | |
var jsonStr1 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key1", "value": "value", "type": "String" }, | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key3", "value": 1, "type": "Integer" } | |
] | |
} | |
"""; | |
var jsonStr2 = | |
""" | |
{ | |
"appconfig": [ | |
{ "value": 1, "key": "key3", "type": "Integer" }, | |
{ "type": "Boolean", "key": "key2", "value": true }, | |
{ "key": "key1", "type": "String", "value": "value" } | |
] | |
} | |
"""; | |
assertTrue(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth2_different_size__false() { | |
var jsonStr1 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key1", "value": "value", "type": "String" }, | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key3", "value": 1, "type": "Integer" } | |
] | |
} | |
"""; | |
var jsonStr2 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key1", "value": "value", "type": "String" } | |
] | |
} | |
"""; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
@Test | |
void compareTwoJsons_depth2_different_keys__false() { | |
var jsonStr1 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key1", "value": "value", "type": "String" }, | |
{ "key": "key2", "value": true, "type": "Boolean" }, | |
{ "key": "key3", "value": 1, "type": "Integer" } | |
] | |
} | |
"""; | |
var jsonStr2 = | |
""" | |
{ | |
"appconfig": [ | |
{ "key": "key4", "value": 1, "type": "Integer" }, | |
{ "key": "key5", "value": true, "type": "Boolean" }, | |
{ "key": "key6", "value": "value", "type": "String" } | |
] | |
} | |
"""; | |
assertFalse(JsonUtils.compareTwoJsons(jsonStr1, jsonStr2)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment