Fastjson1 JSONPath 支持

对于 Java 开发者来说,处理 JSON 数据早已是常规操作。但当 JSON 结构变得复杂(比如多层嵌套或动态字段)时,传统的 getJSONObject()getJSONArray() 方法会显得冗长且易出错。这时候,JSONPath 就像一张高效的索引地图,让你用类似 XPath 的表达式快速定位 JSON 中的任意节点。

Fastjson 1.x 版本直接内置了 JSONPath 支持,无需额外依赖。本文会用最简单的方式,带你快速上手。

什么是 JSONPath?

JSONPath 是一种用于从 JSON 文档中提取数据的表达式语言。
例如:

  • $.store.book[0].title → 获取 store 下第一本书的标题。
  • $..author → 递归查找所有 author 字段。

在 Fastjson 中,调用方式非常统一:JSONPath.eval(jsonObject, path)

基本使用步骤

引入 Fastjson

Maven 依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

 准备 JSON 数据

String jsonStr = "{\n" +
        "  \"store\": {\n" +
        "    \"book\": [\n" +
        "      { \"title\": \"Java 编程思想\", \"price\": 88 },\n" +
        "      { \"title\": \"Effective Java\", \"price\": 98 },\n" +
        "      { \"title\": \"Spring 实战\", \"price\": 78, \"isbn\": \"978-1-234\" }\n" +
        "    ],\n" +
        "    \"bicycle\": { \"color\": \"red\", \"price\": 2000 }\n" +
        "  }\n" +
        "}";

JSONObject root = JSON.parseObject(jsonStr);

常用表达式示例

根据索引访问数组元素

// 获取第一本书的标题
String title = (String) JSONPath.eval(root, "$.store.book[0].title");
System.out.println(title);  // 输出: Java 编程思想

// 获取最后一本书(索引倒数)
Object lastBook = JSONPath.eval(root, "$.store.book[-1:]");
System.out.println(lastBook); // 输出最后一项

按条件过滤

// 查找价格大于 80 的所有书
List<Object> expensiveBooks = (List<Object>) JSONPath.eval(
        root, "$.store.book[?(@.price > 80)]"
);
System.out.println(expensiveBooks.size()); // 2(Java编程思想 + Effective Java)

// 查找有 isbn 字段的书籍
List<Object> withIsbn = (List<Object>) JSONPath.eval(
        root, "$.store.book[?(@.isbn)]"
);
System.out.println(withIsbn.get(0)); // 输出第三本书

通配符与深度遍历

// 获取 store 下所有 price(包括 book 和 bicycle)
List<Object> allPrices = (List<Object>) JSONPath.eval(root, "$.store..price");
System.out.println(allPrices); // [2000, 88, 98, 78]

// 递归查找所有字符串类型的 title
List<Object> allTitles = (List<Object>) JSONPath.eval(root, "$..title");
System.out.println(allTitles); // [Java编程思想, Effective Java, Spring实战]

进阶用法:动态路径组合

有时路径中的索引或属性名需要通过变量来确定:

int index = 1;
String property = "price";

// 动态索引
Object book2Price = JSONPath.eval(root, "$.store.book[" + index + "]." + property);
System.out.println(book2Price); // 98 (第二本书的价格)

// 或者用 eval 支持变量拼接(类型安全)
String path = String.format("$.store.book[%d].%s", index, property);
Object result = JSONPath.eval(root, path);
System.out.println(result); // 同上

完整案例:解析复杂响应

假设有一个 API 返回如下嵌套 JSON,我们需要提取所有用户的邮箱:

{
  "code": 200,
  "data": {
    "users": [
      { "name": "Alice", "contacts": { "email": "[email protected]" } },
      { "name": "Bob", "contacts": { "email": "[email protected]" } }
    ]
  }
}

代码实现:

String response = "..."; // 上述JSON字符串
JSONObject obj = JSON.parseObject(response);

// 深度遍历:任意路径下的 email
List<String> emails = (List<String>) JSONPath.eval(obj, "$..email");
System.out.println(emails); // [[email protected], [email protected]]

// 或精确路径
List<String> emails2 = (List<String>) JSONPath.eval(obj, "$.data.users[*].contacts.email");
System.out.println(emails2); // 同上

注意事项

  1. 类型转换eval() 返回 Object,需要手动转型。建议用泛型集合时加 @SuppressWarnings
  2. 路径语法
    • $ 代表根节点
    • . 访问子节点
    • [] 数组索引或条件过滤器
    • ?() 过滤表达式(支持 >, <, ==, !=
  3. 性能:Fastjson 的 JSONPath 实现经过了优化,千万级数据处理依然高效。

总结

JSONPath 大幅降低了复杂 JSON 的解析难度,尤其适合以下场景:

  • 接口响应字段不固定(如 data 内动态增减属性)
  • 需要按条件提取数组子集(例如获取价格区间内的商品)
  • 递归搜索任意深度的嵌套字段