JSONPath expression to get a value from an array on condition or just the first value

jhericks picture jhericks · Jan 4, 2017 · Viewed 27.4k times · Source

Given JSON structured like this:

{
   "name":"Some Guy",
   "emails":[
      {
         "description":"primary",
         "status":"UNVERIFIED",
         "email":"[email protected]"
      },
      {
         "description":"home",
         "status":"VERIFIED",
         "email":"[email protected]"
      },
      {
         "description":"away",
         "status":"VERIFIED",
         "email":"[email protected]"
      }
   ]
}

I would like a JSONPath expression to get the first email with status VERIFIED and if there are none, then just get the first email in the array. So, given the example above, the result would be [email protected]. Given this example:

{
   "name":"Some Guy",
   "emails":[
      {
         "description":"primary",
         "status":"UNVERIFIED",
         "email":"[email protected]"
      },
      {
         "description":"home",
         "status":"UNVERIFIED",
         "email":"[email protected]"
      }
   ]
}

the result would be [email protected].

Is this possible with a JSONPath expression?

Answer

approxiblue picture approxiblue · Jan 10, 2017

You effectively have 2 JSONPath expressions, where the second one (first email) should be evaluated only if the first one (first verified email) returns nothing, so I don't think you can evaluate them both at the same time, in a single expression.

You can apply them one after the other, though:

public static String getEmail(String json) {
    Configuration cf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).build();
    DocumentContext ctx = JsonPath.using(cf).parse(json);
    List<String> emails = ctx.read("$.emails[?(@.status == 'VERIFIED')].email");
    if (!emails.isEmpty()) {
        return emails.get(0);
    }
    return ctx.read("$.emails[0].email");
}

If the email array is empty, ctx.read("$.emails[0].email") will return null instead of throwing an exception, thanks to the option SUPPRESS_EXCEPTIONS.


If you don't know the number of paths in advance:

public static String getEmail(String json, String[] paths) {
    Configuration cf = Configuration.builder().options(Option.ALWAYS_RETURN_LIST, Option.SUPPRESS_EXCEPTIONS).build();
    DocumentContext ctx = JsonPath.using(cf).parse(json);
    for (String path : paths) {
        List<String> emails = ctx.read(path);
        if (!emails.isEmpty()) {
            return emails.get(0);
        }
    }
    return null;
}

The option ALWAYS_RETURN_LIST means the return type is a list, even when you have one or zero results.