7

I have a hashmap which has key value pair of String and object. It is the conversion of something like below json.

{
    "test1": {
        "test2": {
            "test3": {
                "key": "value"
            },
            "somefields12": "some value2"
        },
        "somefields": "some value"
    }
}

But, I am not converting to map. I have just that map. If this may has key and value , I have to do write some logic based on that value. I implemented as below:

    if (map.containsKey("test1") ) {
        final HashMap<String, Object> test1 = (HashMap<String, Object>) map.get("test1");
        if (test1.containsKey("test2")) {
            final List<Object> test2 = (List<Object>) test1.get("test2");
            if (!test2.isEmpty()) {
                final HashMap<String, Object> test3 = (HashMap<String, Object>) test2.get(0);
                if (test3.containsKey("key")) {
                    final String value = String.valueOf(test2.get("key"));
                    if (!StringUtils.isBlank(value)) {
                        //do some work based on value
                    }
                }
            }
        }
    }

Now, I wanted to avoid the nested if (multiple ifs) from my code. What would be the best way to do so?

Jacob
  • 420
  • 5
  • 15
  • https://stackoverflow.com/questions/2774608/how-do-i-access-nested-hashmaps-in-java – Nick Aug 02 '18 at 21:20
  • If you don't want to use any json parser libraries, you can implement this in the recursive way. – lord_hokage Aug 02 '18 at 21:20
  • Possible duplicate of [How do I access nested HashMaps in Java?](https://stackoverflow.com/questions/2774608/how-do-i-access-nested-hashmaps-in-java) –  Aug 02 '18 at 21:37
  • Are you only interested in the value of `"key"`? Or do you want a solution that can access `"somefields"` also? – Mick Mnemonic Aug 02 '18 at 21:56

3 Answers3

3

I'm not familiar with the fancy new Java 8 features, so I'd do it the old fashioned way with a function that takes a path to look up, and walks the list with a loop:

import java.util.*;

class Test {

  static String getByPath(HashMap<String, Object> map, String... path) {
    for(int i=0; i<path.length-1; i++) {
      map = (HashMap<String, Object>) map.get(path[i]);
      if (map == null) return null;
    }
    Object value = map.get(path[path.length-1]);
    return value == null ? null : String.valueOf(value);
  }

  public static void main(String[] args) {
    HashMap<String, Object> map = new HashMap<>();
    HashMap<String, Object> tmp1 = new HashMap<>();
    HashMap<String, Object> tmp2 = new HashMap<>();
    map.put("test1", tmp1);
    tmp1.put("test2", tmp2);
    tmp2.put("key1", "My Value");

    System.out.println("With valid path:   " + getByPath(map, "test1", "test2", "key1"));
    System.out.println("With invalid path: " + getByPath(map, "test1", "BANANA", "key1"));
  }
}

This results in:

With valid path:   My Value
With invalid path: null

This can optionally be extended to:

  • Check that nodes are in fact maps before casting
  • Use Optional or a helpful exception instead of returning null
that other guy
  • 116,971
  • 11
  • 170
  • 194
1

Using Gson library combined with java 8 Optional you can do something like this:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.Optional;

public class FindJsonKey {
    public static final String JSON = "{\n" +
            "  \"test1\": {\n" +
            "    \"test2\": {\n" +
            "      \"test3\": {\n" +
            "        \"key\": \"value\"\n" +
            "      },\n" +
            "      \"somefields12\": \"some value2\"\n" +
            "    },\n" +
            "    \"somefields\": \"some value\"\n" +
            "  }\n" +
            "}";

    public static void main(String[] args) {

        Gson gson = new GsonBuilder().create();

        JsonObject jsonObject = gson.fromJson(JSON, JsonObject.class);

        Optional
                .ofNullable(jsonObject.getAsJsonObject("test1"))
                .map(test1 -> test1.getAsJsonObject("test2"))
                .map(test2 -> test2.getAsJsonObject("test3"))
                .map(test3 -> test3.get("key"))
                .map(JsonElement::getAsString)
                .ifPresent(key -> {
                    // Do something with key..
                });
    }
}
  • This looks quite fluent. It would of course be best if OP could use a JSON parser directly instead of having to work with a passed-in nested map structure. – Mick Mnemonic Aug 04 '18 at 22:35
0

A recursive solution that assumes the JSON structure is as given -- and ignores the names of the nested parent maps -- could look like the following:

/**
 * Traverses the map from {@code nextEntry} until a key matching {@code keyTofind} is found 
 * or the bottom of the map is reached. 
 * The method assumes that any nested maps are contained in the first 
 * element of the parent map.
 *  
 * @param nextEntry the next entry to search from
 * @param keyToFind the stopping condition for the recursion
 * @return the value matching {@code keyToFind} (may be empty)
 */
private static Optional<Object> find(Map.Entry<String,Object> nextEntry, String keyToFind) {

    Object value = nextEntry.getValue();

    if (nextEntry.getKey().equals(keyToFind)) {
        return Optional.of(value);
    } else if (nextEntry.getValue() instanceof Map) {
        return find(((Map<String, Object>)nextEntry.getValue())
                      .entrySet().iterator().next(), keyToFind);
    } else {
        return Optional.empty();
    }
}

Testing with the provided data (with help of Jackson for JSON parsing)

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Test {

    private static final String JSON = "{ "
            + "\"test1\": {"
            + "  \"test2\": {"
            + "      \"test3\": {"
            + "       \"key\": \"value\""
            + "},"
            + " \"somefields12\": \"some value2\""
            + "},"
            + " \"somefields\": \"some value\""
            + "}"
            + "}";

    //...

    public static void main(String[] args) throws Exception {

        Map<String, Object> json = new ObjectMapper()
            .readValue(JSON, new TypeReference<Map<String, Object>>() { });

        final String keyToFind = "key";
        Optional<Object> value = find(json.entrySet().iterator().next(), keyToFind);
        System.out.println(value.isPresent() ? value.get() : "null");
   }
}

gives

value
Mick Mnemonic
  • 7,808
  • 2
  • 26
  • 30