How to remove the empty objects and null values present in nested complex LinkedHashMap?

Viewed 296

I have a complex nested LinkedHashMap for which I am assigning the values coming from another application. Some of the fields can have Null values since the values may be missing in input JSON.

I am looking for an approach to omit all the Null & Empty values present within my Complex/Nested LinkedHashMap. If it's a direct key, value LinkedHashMap then the process is quite straightforward forward but in my case, the LinkedHashMap value can have another LinkedHashmap within it.

Is there any simple, optimal Java 8 streams way to remove all empty/null values even within nested LinkedHashMap?

Following is the sample LinkedHashMap structure I have:

package io.template;

import java.util.LinkedHashMap;

public class TemplateNodeMap extends LinkedHashMap {

    public TemplateNodeMap() {
        put("type", null);
        
        put("step", null);

        put("error", new LinkedHashMap<>() {{
            put("time", null);
            put("errorId", new LinkedHashMap<>() {{
                put("correctId", null);
            }});
        }});

        put("sensor", new LinkedHashMap<>() {{
            put("metadata", new LinkedHashMap<>() {{
                put("time", null);
                put("startTime", null);
            }});

            put("report", new LinkedHashMap<>() {{
                put("type", null);
                put("device", null);
                put("deviceMetadata", null);
            }});
        }});
    }
}

Currently, I have written a recursive code something like this:

public LinkedHashMap removeNull(LinkedHashMap map) {
    LinkedHashMap templateNodeMap = new LinkedHashMap();

    map.forEach((key, value) -> {
        if (value != null && value instanceof LinkedHashMap) {
            templateNodeMap.put(key, removeNull((LinkedHashMap) value));
        } else if (value != null) {
            templateNodeMap.put(key, value);
        }
    });
    return templateNodeMap;
}

The above code is working for null but it does not remove the empty values. For some fields, I am getting the value as sensor={metadata={},report={}}}, I would like to remove fields altogether if it does not have any values. I do not wish to get even the empty object {}.

Can someone please let me know if there is a way to do it in Java Streams or in a recursive approach. Thanks in advance.

3 Answers

Based on your attempt , I can suggest some addition :

map.forEach((key, value) -> {
        if (value != null && value instanceof LinkedHashMap) {
            templateNodeMap.put(key, removeNull((LinkedHashMap) value));
            templateNodeMap.compute(key, (x,y)->(y.isEmpty()) ? null : y); 
        } else if (value != null) {
            templateNodeMap.put(key, value);
        }
    });

Once you returned from recursion inside if block , you can compute for empty values of mapping & remove if it contains empty values.

You can declare a variable to store the results of your removeNull() method inside the if statement that is making a recursive call to the method. You then check if the returned LinkedHashMap is not empty before adding it to the result LinkedHashMap like:

public LinkedHashMap removeNull(LinkedHashMap map) {
    LinkedHashMap templateNodeMap = new LinkedHashMap();

    map.forEach((key, value) -> {
        if (value != null && value instanceof LinkedHashMap) {
            LinkdedHashMap hashMap = removeNull((LinkedHashMap) value);
            if(!hashMap.isEmpty()) {
               templateNodeMap.put(key, hashMap);
            }
        } else if (value != null) {
            templateNodeMap.put(key, value);
        }
    });
    return templateNodeMap;
}

A simple void method:

public static void removeNull(Map<?, ?> map) {
    map.values().forEach(e -> {
        if (e instanceof Map)
            removeNull((Map<?, ?>) e);
    });
    map.values().removeIf(e -> e == null || e instanceof Map && ((Map<?, ?>) e).isEmpty());
}

And call it with removeNull(test);

Related