Search code examples
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);
            }
        }
    }