Search code examples
javajsonresthamcrest

Verify the existence of an array of root nodes with JSONpath


From the below JSON response, I can verify that a root node exists the in JSONpath using this method from hamcrest library:

assertThat(json, hasJsonPath("$.tool"));

This checks that the root node called 'tool' exists.

{
    "tool": 
    {
        "jsonpath": 
        {
            "creator": 
            {
                "name": "Jayway Inc.",
                "location": 
                [
                    "Malmo",
                    "San Francisco",
                    "Helsingborg"
                ]
            }
        }
    },

    "book": 
    [
        {
            "title": "Beginning JSON",
            "price": 49.99
        },

        {
            "title": "JSON at Work",
            "price": 29.99
        }
    ]
}

If I want to check the existence of the two root nodes (tool and book)using an array stored in a variable, how can this be achieved? I am not concerned about their values or the values of the sub nodes. I just want to verify that those 2 root nodes exist in the response and are valid paths.

After stringing my API response in a 'json' variable I tried something like this:

JsonPath jp = new JsonPath(json);

String[] rootNodes = {"tool", "book"};
assertThat(json, hasJsonPath(json.getString(rootNodes)));

But compiler was unhappy with the getString method.

Is there a way to resolve this issue?


Solution

  • The root node operator is $ so you can just read $ into a map and that map will be keyed on your root node names.

    For example:

    // Approach 1
    Map<String, Object> read = JsonPath.read(json, "$");
    
    assertThat(read.size(), is(2));
    
    assertThat(read.keySet(), hasItem("tool"));
    assertThat(read.keySet(), hasItem("book"));
    
    // Approach 2: if you want a String[] then ...
    String[] rootNodeNames = read.keySet().toArray(new String[read.size()]);
    assertThat(rootNodeNames, Matchers.both(arrayWithSize(2)).and(arrayContainingInAnyOrder("book", "tool")));
    
    // Approach 3: if you want to hide it all behind the hasJsonPath() matcher
    // note: each of these assertion will cause your JSON string to be parsed 
    // so it's more efficient to do that once and assert against the 
    // resulting map as I did above
    assertThat(json, hasJsonPath("$", Matchers.hasKey("tool")));
    assertThat(json, hasJsonPath("$", Matchers.hasKey("book")));