根据条件获取数组中的值或仅获取第一个值的JSONPath表达式

10
{"people": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]}

提供类似以下结构的JSON:


{"people": [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]}
{
   "name":"Some Guy",
   "emails":[
      {
         "description":"primary",
         "status":"UNVERIFIED",
         "email":"first@first-email.com"
      },
      {
         "description":"home",
         "status":"VERIFIED",
         "email":"second@second-email.com"
      },
      {
         "description":"away",
         "status":"VERIFIED",
         "email":"third@third-email.com"
      }
   ]
}

我希望能够使用一个JSONPath表达式来获取状态为VERIFIED的第一个电子邮件,如果没有,则只获取数组中的第一个电子邮件。因此,鉴于上面的示例,结果将是second@second-email.com。给定以下示例:

{
   "name":"Some Guy",
   "emails":[
      {
         "description":"primary",
         "status":"UNVERIFIED",
         "email":"first@first-email.com"
      },
      {
         "description":"home",
         "status":"UNVERIFIED",
         "email":"second@second-email.com"
      }
   ]
}

结果将会是first@first-email.com

使用JSONPath表达式是否可行?


你使用的是哪个JSONPath实现?你需要在一个表达式中完成吗? - approxiblue
今天我正在使用jayway Java实现,但如果您有其他可以工作的实现,我可以想办法将其集成。我考虑用一个表达式完成,但如果你有一种可以用一系列表达式表示的方法,我也可以考虑。只是不能要求我自己编写Java或Javascript代码来处理中间结果。 - jhericks
2个回答

16
您实际上有两个JSONPath表达式,第二个表达式(第一个电子邮件)只应在第一个表达式(第一个已验证电子邮件)返回空时进行评估,因此我认为您不能同时在单个表达式中评估它们。但是,您可以依次应用它们:
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");
}

如果电子邮件数组为空,ctx.read("$.emails[0].email") 将返回 null 而不是抛出异常,这得益于 选项 SUPPRESS_EXCEPTIONS
如果您事先不知道路径数量:
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;
}

选项ALWAYS_RETURN_LIST表示返回类型是列表,即使你只有一个或零个结果。


问题在于我只有一些JSON输入和一个将键映射到JSONPath表达式的地图。 我不知道这些表达式最初是什么-它们是可配置的,所以像getEmail方法这样的东西对我来说行不通。 - jhericks
@jhericks 这个方法更多是为了演示。您可以将这两个表达式依次应用,以便将我示例中的两个硬编码表达式转换为参数。 - approxiblue
@jhericks 你可以使用 ALWAYS_RETURN_LIST 来确保所有读取操作都返回一个列表类型,接着遍历这些路径即可。回答已更新。 - approxiblue
实际上,我还有一个后续问题。是否有一种方法可以修改$.emails [?(@.status == 'VERIFIED')].email表达式本身,以仅返回第一个匹配项(而不是列表)?我知道$.emails [?(@.status == 'VERIFIED')].email [0]不起作用,但这就是我要找的东西。 - jhericks
1
@jhericks 如果要将过滤器与索引访问结合起来,有一个未解决的问题:https://github.com/jayway/JsonPath/issues/272 - approxiblue
显示剩余3条评论

-1

这段代码应该对你来说完美运行

//Use the json parsing library to extract the data
    JsonPath jp = new JsonPath(json); 
    Map<String, Object> location = jp.get("name.emails[0]");
    System.out.println(location);

这并没有考虑到 email.status。 - jhericks
应该可以做到。将第一条记录的所有参数捕获。打印“位置”到控制台以进行验证。 - user8715994
这里的另一个问题是您不能假定条目的顺序。JSon数组类似于JSon无序列表。 - Jaco Van Niekerk
我并不是说 'status' 不会被包含在内。我的意思是它只会始终获取第一个电子邮件,而不考虑 status == 'VERIFIED' 是否成立。 对于给出的两个 JSON 示例,它会给出不同的结果吗? - jhericks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接