0

I have an ObjectNode, that looks as follows

    {
     "Header":{
       "sub-header1":{
          "#field":"value",
          "#field":"value",

       },
       "sub-header2":{
          "field":"",
          "field":"",
          "field":"",
          "panel_field":{
            "value":"",
            "value":""
         }
      }          
   }

Now, what I want to do is to get all the fields from sub-headers in a list. This is the method that I'm employing

public static List<String> getDocumentFields(ObjectNode jsonDocumentNode) {        
    List<String> documentFields = new ArrayList<>();
    Iterator<JsonNode> fields = jsonDocumentNode.elements();
    while (fields.hasNext()) {
        JsonNode jsonNode = fields.next();
        Iterator<Map.Entry<String, JsonNode>> jsonFields = jsonNode.fields();
        while (jsonFields.hasNext()) {
            Map.Entry<String, JsonNode> jsonNodeEntry = jsonFields.next();
            documentFields.add(jsonNodeEntry.getKey());
        }
    }
    return documentFields;
}

But, I'm only getting headers in the list like {sub-header1, sub-header2}, instead of fields. How can I fix this? I'd really appreciate any kind of help.

EDIT: While @sfiss's answer helped a great deal, I still wanted to find a way to do so without hardcoding the loop-logic and this answer turned out to be the exact thing I was looking for.

Roshan Upreti
  • 1,782
  • 3
  • 21
  • 34

1 Answers1

0

Well, it's simple, you are not iterating deep enough (field list is on third level). If you know the structure of your JSON, just iterate until you find the desired fields:

public class MyTest {

    @Test
    public void testJson() throws IOException {
        final String json = getJson();
        final JsonNode jsonDocumentNode = new ObjectMapper().readTree(json);
        final List<String> fields = getDocumentFields((ObjectNode) jsonDocumentNode);
        assertThat(fields, Matchers.contains("#field1", "#field2", "field1", "field2", "field3", "panel_field"));
    }

    public static String getJson() {
        return "{\r\n" +
                "     \"Header\":{\r\n" +
                "       \"sub-header1\":{\r\n" +
                "          \"#field1\":\"value\",\r\n" +
                "          \"#field2\":\"value\"\r\n" +
                "       },\r\n" +
                "       \"sub-header2\":{\r\n" +
                "          \"field1\":\"\",\r\n" +
                "          \"field2\":\"\",\r\n" +
                "          \"field3\":\"\",\r\n" +
                "          \"panel_field\":{\r\n" +
                "            \"value1\":\"\",\r\n" +
                "            \"value2\":\"\"\r\n" +
                "         }\r\n" +
                "      }          \r\n" +
                "   }\r\n" +
                "}";
    }

    public static List<String> getDocumentFields(final ObjectNode jsonDocumentNode) {
        final List<String> documentFields = new ArrayList<>();
        for (final JsonNode header : (Iterable<JsonNode>) jsonDocumentNode::elements) {
            for (final JsonNode subheader : (Iterable<JsonNode>) header::elements) {
                for (final Map.Entry<String, JsonNode> field : (Iterable<Map.Entry<String, JsonNode>>) subheader::fields) {
                    documentFields.add(field.getKey());
                }
            }
        }
        return documentFields;
    }
}

However, I would argue that it is simpler to let jackson serialize the JSON into a convenient data structure, and you just use the POJO's getters to obtain your values. That would also make it more clear than handling the JsonNode.

Just FYI, I edited your JSON slightly and used Java 8 SAM conversions to create iterables for the foreach-loops from the iterators, but you can still use your code and iterate one more level using while and iterators.

sfiss
  • 2,119
  • 13
  • 19