Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the first element when filter in jsonpath?

Tags:

java

jsonpath

So I'm working on the below json:

{
   "id": "",
   "owner": "some dude",
   "metaData": {
      "request": {
         "ref": null,
         "contacts":[
            {
               "email": null,
               "name": null,
               "contactType": "R"
            },
            {
               "email": null,
               "name": "Dante",
               "contactType": "S"
            }
         ]
      }
   }
}

I want to retrieve the name of contact has type S and only the first one that returned.

Using jsonpath with this path "$..contacts[?(@.contactType == 'S')].name" always return an array of string because a filter operation always return result as an array.

So I tried "$..contacts[?(@.contactType == 'S')].name[0]" and "$..contacts[?(@.contactType == 'S')][0].name" but no luck. Those path returns empty results.

So my question is, is there any way to get only first element when using filter in jsonpath. I'm currently using jayway jsonpath v2.2.0.

like image 278
Vu Viet Dung Avatar asked Jan 08 '17 04:01

Vu Viet Dung


Video Answer


2 Answers

If you use jsonpath with MockMvc class from spring-test, then you may write the following dummy matcher:

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.core.IsEqual;

import net.minidev.json.JSONArray;

public class FirstMatcher<T> extends BaseMatcher<T> {

    private final Matcher<?> matcher;

    public static <T> FirstMatcher<T> matcher(Matcher<T> matcher) {
        return new FirstMatcher<T>(matcher);
    }

    public static FirstMatcher<Object> value(Object value) {
        return new FirstMatcher<Object>(value);
    }

    public FirstMatcher(Matcher<T> matcher) {
        this.matcher = matcher;
    }

    public FirstMatcher(Object value) {
        this.matcher = new IsEqual<Object>(value); 
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("first matcher");
    }

    @Override
    public boolean matches(Object item) {
        if (!(item instanceof JSONArray)) {
            return false;
        }

        JSONArray array = (JSONArray)item;
        if (array.isEmpty()) {
            return false;
        }

        Object obj = array.get(0);
        return matcher.matches(obj);
    }

}

And use the following way:

mockMvc.
    perform(get(url).
            accept(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_PLAIN).accept(MediaType.ALL)).
    andExpect(status().isOk()).
    andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
    andExpect(jsonPath("$.properties[?(@.propertyName == 'name1')].description").value(FirstMatcher.matcher(IsNull.nullValue()))).
    andExpect(jsonPath("$.properties[?(@.propertyName == 'name2')].required").value(FirstMatcher.value(true)));

P.S. Since net.minidev.json.JSONArray subclasses java.util.List, one may cast to List or even Iterable rather than to net.minidev.json.JSONArray. :)

like image 91
capocannoniere Avatar answered Oct 16 '22 23:10

capocannoniere


The quickest solution I found was just comparing it with a list, so like this in your scenario:

.andExpect(jsonPath("$..contacts[?(@.contactType == 'S')].name", equalTo(Arrays.asList("Dante"))))

(Assuming you are trying to compare some result in a test of course)

like image 30
Vegard Avatar answered Oct 17 '22 00:10

Vegard