-1

I have the following json file test.json

{
  "type" : "object",
  "id" : "123",
  "title" : "Test",
  "properties" : {
    "_picnic" : {
      "type" : "boolean"
....

I am able to get the first level value of the key using the following code. E.g. for type I get object

public static void main(String[] args){

        String exampleRequest = null;
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            exampleRequest = FileUtils.readFileToString(new File("src/main/resources/test.json"), StandardCharsets.UTF_8);
            JsonNode jsonNode = objectMapper.readTree(exampleRequest);

            String type = jsonNode.get("type").asText();
            System.out.println(type);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // System.out.println(exampleRequest);


    }

However I do not get any response when I try to get the value for the second level. E.g. if I do

String type = jsonNode.get("_picnic").asText();
System.out.println(type);

OR the whole object e.g.

String type = jsonNode.get("properties").asText();
System.out.println(type);

I tried doing properties._picnic or properties[0]_picnic but no luck.

Dr. Mian
  • 3,334
  • 10
  • 45
  • 69
  • Maybe this could help you https://stackoverflow.com/questions/14898768/how-to-access-nested-elements-of-json-object-using-getjsonarray-method – DaveR Apr 12 '23 at 18:44

2 Answers2

2

The documentation for that method states (emphasis mine):

Method that will return a valid String representation of the container value, if the node is a value node (method isValueNode() returns true), otherwise empty String.

Per the snippet of json text you shared, both "properties" and "_picnic" are objects not values so you are getting an empty string. It seems you'd need to use the get method:

jsonNode.get("properties").get("_picnic").get("type").asText()
dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • Thanks for reply, it is getting the value of type but why dont it gives me a block fro `jsonNode.get("properties").get("_picnic").asText()` or `jsonNode.get("properties").asText()` – Dr. Mian Apr 12 '23 at 19:59
  • 1
    I already explained this in my answer, but I'll update to clarify a bit. – dominicoder Apr 12 '23 at 20:07
  • Okay so you mentioned `get` but did not use it. Could you add this with the answer too. To get the respective objects use `String type = String.valueOf(jsonNode.get("properties"));` and `String type = String.valueOf(jsonNode.get("properties").get("_picnic"))`; – Dr. Mian Apr 13 '23 at 08:20
  • I did show an example of get in my original answer. `String type = String.valueOf(jsonNode.get("properties"));` would not be correct since in the JSON you posted `"properties"` is an object. – dominicoder Apr 13 '23 at 14:26
1

You can use recursion to simplify this code. Just call getText with a starting node and a variable list (array) of paths. It will determine if it need to check for object or array.

import java.io.*;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import com.fasterxml.jackson.databind.*;

public class NestedJson {
    public static void main(String[] args) {
        String exampleRequest = null;
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            exampleRequest = FileUtils.readFileToString(new File("src/main/resources/test.json"),
                    StandardCharsets.UTF_8);
            JsonNode jsonNode = objectMapper.readTree(exampleRequest);
            System.out.println(getText(jsonNode, "type")); // object
            System.out.println(getText(jsonNode, "properties", "_picnic", "type")); // boolean
            System.out.println(getText(jsonNode, "stuff", "0", "foo")); // bar
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getText(JsonNode node, String... path) {
        return getText(node, 0, path);
    }

    private static String getText(JsonNode node, int cursor, String[] path) {
        if (node == null || path == null || path.length == 0)
            return null;
        if (cursor == path.length) {
            return node != null ? node.asText() : null;
        }
        if (node.isObject()) {
            return getText(node.get(path[cursor]), cursor + 1, path);
        } else if (node.isArray()) {
            return getText(node.get(Integer.parseInt(path[cursor])), cursor + 1, path);
        }
        return null;
    }
}

Modified test.json:

{
  "type": "object",
  "id": "123",
  "title": "Test",
  "properties": {
    "_picnic": {
      "type": "boolean"
    }
  },
  "stuff": [
    {
      "foo": "bar"
    }
  ]
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Thanks, I will test it and report back. – Dr. Mian Apr 12 '23 at 20:01
  • I think eventually I am looking for this kind of solution but instead of passing the arguments hardcoded, it would be nice to use the code to find if the coming item is an array or object and accordingly call the function. – Dr. Mian Apr 13 '23 at 08:29