【问题标题】:JSONPath expression to get a value from an array on condition or just the first valueJSONPath 表达式在条件下从数组中获取值或仅获取第一个值
【发布时间】:2017-01-04 18:53:58
【问题描述】:

鉴于 JSON 结构如下:

{
   "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 expression 获得状态为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 实现?您需要在一个表达式中执行此操作吗?
  • 今天我使用的是 jayway Java 实现,但如果您有其他可行的实现,我可以弄清楚如何集成该实现。我想用一个表达式来做是的,但是如果你有一种方法可以将它表达为一系列表达式,我愿意考虑这一点。它只是不能要求我自己的 Java 或 Javascript 代码来处理中间结果。

标签: json jsonpath


【解决方案1】:

您实际上有 2 个 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 而不是抛出异常,这要感谢 option 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;
}

optionALWAYS_RETURN_LIST 表示返回类型是一个列表,即使您有一个或零个结果。

【讨论】:

  • 问题是我只有一些 JSON 输入和 JSONPath 表达式的映射键。我不知道前面的表达式是什么——它们是可配置的,所以像getEmail 这样的方法对我不起作用。
  • @jhericks 方法更多是为了演示。您可以一个接一个地应用这两个表达式,因此您可以将我示例中的两个硬编码表达式都转换为参数。
  • 假设我有一个表达式数组:["$.emails[?(@.status == 'VERIFIED')].email", "$.emails[0].email"] 一个只会遍历这些表达式并给出正确电子邮件的方法会是什么样子?
  • @jhericks 您使用ALWAYS_RETURN_LIST 确保所有读取都返回列表类型,然后循环遍历路径。答案已更新。
  • @jhericks 对于将过滤器与索引访问相结合,有一个未解决的问题:github.com/jayway/JsonPath/issues/272
【解决方案2】:

这段代码应该很适合你

//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。
  • 应该可以。将捕获第一条记录的所有参数。将“位置”打印到控制台以进行验证
  • 这里的另一个问题是您不能假设条目的顺序。一个 JSon 数组类似于一个 JSon 无序列表。
  • 我并不是说不包括“状态”。我的意思是它总是会收到第一封电子邮件,而不考虑 status == 'VERIFIED' 与否。对于给出的两个 JSON 示例,它会给出不同的结果吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-11
  • 2016-05-22
  • 2016-12-16
  • 1970-01-01
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多