javajsonjacksonjackson2

How to compare two JSON String by data type of values, it should ignore exact values and it should also ignore length of array as per below example


Here i have used library

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.0'

while comparing JSON by below code

String actualResponse = "{\n" +
            "    \"employee\":\n" +
            "    {\n" +
            "        \"id\": \"1212\",\n" +
            "        \"fullName\": \"John Miles\",\n" +
            "        \"age\": 34,\n" +
            "        \"skills\": [\"Java\", \"C++\", \"Python\"]\n" +
            "    }\n" +
            "}";

        String expectedResponse = "{\n" +
                "    \"employee\":\n" +
                "    {\n" +
                "        \"id\": \"1212\",\n" +
                "        \"age\": 34,\n" +
                "        \"fullName\": \"John Miles\",\n" +
                "        \"skills\": [\"Java\", \"C++\"] \n" +
                "    } \n" +
                "}";
ObjectMapper mapper = new ObjectMapper();

        JsonNode actualObj1 = mapper.readTree(actualResponse);
        JsonNode actualObj2 = mapper.readTree(expectedResponse);

        assertEquals(actualObj1, actualObj2);

by running above code, the Assertion is failing

Exception in thread "main" java.lang.AssertionError: expected:<{"employee":{"id":"1212","fullName":"John Miles","age":34,"skills":["Java","C++","Python"]}}> but was:<{"employee":{"id":"1212","age":34,"fullName":"John Miles","skills":["Java","C++"]}}>
    at org.junit.Assert.fail(Assert.java:89)
    at org.junit.Assert.failNotEquals(Assert.java:835)
    at org.junit.Assert.assertEquals(Assert.java:120)
    at org.junit.Assert.assertEquals(Assert.java:146)
    at JsonComparision.EndpointJsonMatcher.verifyJsonInteraction(EndpointJsonMatcher.java:47)
    at JsonComparision.EndpointJsonMatcher.main(EndpointJsonMatcher.java:37)

in above example Assertion is failing because length of skills array is mismatching, i am searching for a solution so that it can ignore array length while comparing, and it should only compare by data type of value.


Solution

  • import java.util.HashMap;
    
    import static JsonNeastedComp.JsonStructure.GetJsonMap;
    
    public class CompareJsonStruct1 {
    
        public static void main(String[] args) throws Exception {
    
            String actual = "{\n" +
            "    \"employee\":\n" +
            "    {\n" +
            "        \"id\": \"1212\",\n" +
            "        \"fullName\": \"John Miles\",\n" +
            "        \"age\": 34,\n" +
            "        \"skills\": [\"Java\", \"C++\", \"Python\"]\n" +
            "    }\n" +
            "}";
    
            String expected = "{\n" +
                "    \"employee\":\n" +
                "    {\n" +
                "        \"id\": \"1212\",\n" +
                "        \"age\": 34,\n" +
                "        \"fullName\": \"John Miles\",\n" +
                "        \"skills\": [\"Java\", \"C++\"] \n" +
                "    } \n" +
                "}";
    
            HashMap actual_structure = GetJsonMap(actual);
    
            HashMap expected_structure = GetJsonMap(expected);
    
            boolean result = expected_structure.equals(actual_structure);
    
                if (result) {
                    System.out.println("Matched!");
                } else {
                    System.out.println("Un-matched!");
                }
        }    
    }
    
    
    import org.json.JSONArray;
    import org.json.JSONObject;
    import org.testcontainers.shaded.com.fasterxml.jackson.core.type.TypeReference;
    import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class JsonStructure {
    
        static HashMap actual_map;
    
        public static HashMap GetJsonMap(String actual) throws Exception {
    
            actual_map = new HashMap();
            ObjectMapper objectMapper = new ObjectMapper();
    
            List<String> all_keys_actual = getKeysInJsonUsingMaps(actual, objectMapper);
            all_keys_actual = all_keys_actual.stream().distinct().collect(Collectors.toList());
    
            JSONObject inputJSONOBject = new JSONObject(actual);
    
            for (String key : all_keys_actual) {
                getValueOf(inputJSONOBject, key);
            }
            return actual_map;
        }
    
    // In HashMap, Actual Value will be replaced by it's Datatype
    
        public static void parseObject(JSONObject json, String key) {
    
            if(json.get(key) instanceof JSONArray){
                actual_map.put(key,"JSONArray");
            } else if (json.get(key) instanceof JSONObject) {
                actual_map.put(key,"JSONObject");
            } else if(json.get(key) instanceof String){
                actual_map.put(key,"String");
            } else if(json.get(key) instanceof Integer){
                actual_map.put(key,"Integer");
            }
    
        }
        public static List<String> getKeysInJsonUsingMaps(String json, ObjectMapper mapper) throws Exception {
            List<String> keys = new ArrayList<>();
            Map<String, Object> jsonElements = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
            });
            getAllKeys(jsonElements, keys);
            return keys;
        }
    
    
    
    // Getting all the keys (including nested keys)
        public static void getAllKeys(Map<String, Object> jsonElements, List<String> keys) {
    
            jsonElements.entrySet()
                    .forEach(entry -> {
                        keys.add(entry.getKey());
                        if (entry.getValue() instanceof Map) {
                            Map<String, Object> map = (Map<String, Object>) entry.getValue();
                            getAllKeys(map, keys);
                        } else if (entry.getValue() instanceof List) {
                            List<?> list = (List<?>) entry.getValue();
                            list.forEach(listEntry -> {
                                if (listEntry instanceof Map) {
                                    Map<String, Object> map = (Map<String, Object>) listEntry;
                                    getAllKeys(map, keys);
                                }
                            });
                        }
                    });
        }
    
        // Getting value of all the keys (including nested keys value)
        public static void getValueOf(JSONObject json, String key) {
            boolean exists = json.has(key);
            Iterator< ? > keys;
            String nextKeys;
            if (!exists) {
                keys = json.keys();
                while (keys.hasNext()) {
                    nextKeys = (String) keys.next();
                    try {
                        if (json.get(nextKeys) instanceof JSONObject) {
                            if (exists == false) {
                                getValueOf(json.getJSONObject(nextKeys), key);
                            }
                        } else if (json.get(nextKeys) instanceof JSONArray) {
                            JSONArray jsonarray = json.getJSONArray(nextKeys);
                            for (int i = 0; i < jsonarray.length(); i++) {
                                String jsonarrayString = jsonarray.get(i).toString();
                                JSONObject innerJSOn = new JSONObject(jsonarrayString);
                                if (exists == false) {
                                    getValueOf(innerJSOn, key);
                                }
                            }
                        }
                    } catch (Exception e) {
                    }
                }
            }
            else {
                parseObject(json, key);
            }
        }
    }