Spring Boot 中使用 JSONPath 高效处理 JSON 数据

在日常的后端开发工作中,我们经常需要和 JSON 数据打交道,尤其是要从层级复杂的 JSON 结构里精准提取特定字段。

传统的处理方式,比如借助 Gson、Jackson 这类主流 JSON 解析库的 API,虽然功能完备,但面对复杂路径的字段提取时,写出来的代码往往又长又乱,后期维护起来也特别费劲。

今天就给大家分享一个更优雅的解决方案 ------ JSONPath,它就好比 JSON 领域的 XPath,能让我们用简洁的路径表达式,轻松定位和提取 JSON 里的数据。

02

什么是 JSONPath?

JSONPath 是一种专门用于从 JSON 文档中提取指定数据的查询语言。

它的语法简洁又直观,和 JavaScript 访问对象属性的方式很相似,能快速定位 JSON 结构里的任意数据节点。

核心语法说明:

  • $:代表 JSON 结构的根节点

  • @:代表当前节点

  • .:子节点操作符

  • ..:递归下降,可匹配任意深度的节点

  • *:通配符,匹配所有成员/数组元素

  • []:下标运算符,用于数组索引

  • [start:end]:数组切片,截取指定范围的数组元素

  • [?()]:过滤表达式,按条件筛选数据

03

Spring Boot 集成 JSONPath

想要在 Spring Boot 项目中使用 JSONPath,首先需要在 pom.xml 中引入对应的依赖:

复制代码
<!-- JSONPath 核心依赖 -->
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.9.0</version>
</dependency>

先准备一个示例 JSON 数据,方便后续演示:

复制代码
{
  "store": {
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

基础使用示例(添加详细注解):

复制代码
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import java.util.List;
import java.util.Map;

public class JsonPathExample {

    // 示例JSON字符串(实际使用时替换为真实数据)
    private String json = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}";

    public void testReadJson() {
        // 1. 提取所有书籍的作者
        List<String> authors = JsonPath.parse(json)
            .read("$.store.book[*].author");
        
        // 2. 提取第一本书的价格
        Double price = JsonPath.parse(json)
            .read("$.store.book[0].price");
        
        // 3. 过滤价格低于10元的书籍
        List<Map<String, Object>> cheapBooks = JsonPath.parse(json)
            .read("$.store.book[?(@.price < 10)]");
        
        // 4. 提取最后一本书的完整信息
        Map<String, Object> lastBook = JsonPath.parse(json)
            .read("$.store.book[-1]");
    }
}

在 Spring Boot 接口中实战使用:

复制代码
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;

@RestController
public class BookController {

    /**
     * 提取JSON中的书籍标题和指定价格区间的书籍
     * @param jsonString 传入的JSON字符串
     * @return 包含标题和过滤后书籍的响应
     */
    @PostMapping("/extractBookData")
    public ResponseEntity<?> extractData(@RequestBody String jsonString) {
        try {
            // 提取所有书籍的标题
            List<String> titles = JsonPath.parse(jsonString)
                .read("$.store.book[*].title");

            // 过滤价格大于等于8且小于等于15的书籍(补全原代码语法错误)
            List<Map<String, Object>> books = JsonPath.parse(jsonString)
                .read("$.store.book[?(@.price >= 8 && @.price <= 15)]");

            return ResponseEntity.ok(Map.of(
                "titles", titles,
                "filteredBooks", books
            ));
        } catch (PathNotFoundException e) {
            // 路径不存在时返回错误提示
            return ResponseEntity.badRequest()
                .body("JSON路径不存在: " + e.getMessage());
        }
    }

    /**
     * 提取所有书籍的作者
     * @param jsonData 传入的JSON字符串
     * @return 作者列表
     */
    @PostMapping("/getBookAuthors")
    public ResponseEntity<?> getAuthors(@RequestBody String jsonData) {
        List<String> authors = JsonPath.parse(jsonData)
            .read("$.store.book[*].author");
        return ResponseEntity.ok(authors);
    }
}

自定义 JSONPath 配置(适配不同业务场景):

复制代码
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Option;

public class JsonPathConfig {

    /**
     * 构建自定义的JSONPath配置
     * @return 配置实例
     */
    public Configuration jsonPathConfiguration() {
        return Configuration.builder()
            // 抑制异常(路径不存在时不抛异常)
            .options(Option.SUPPRESS_EXCEPTIONS)
            // 路径叶子节点不存在时返回null
            .options(Option.DEFAULT_PATH_LEAF_TO_NULL)
            // 始终返回List类型(即使结果只有一个元素)
            .options(Option.ALWAYS_RETURN_LIST)
            // 开启缓存(提升重复路径查询性能)
            .options(Option.CACHE)
            .build();
    }
}

优化 JSONPath 查询性能(缓存与预编译):

复制代码
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class JsonPathCacheService {

    // 缓存容器,存储已解析的JSON数据(线程安全)
    private final Map<String, Object> cache = new ConcurrentHashMap<>();

    /**
     * 带缓存的JSONPath读取(基础版)
     * @param json 原始JSON字符串
     * @param path JSONPath表达式
     * @return 提取的数据
     */
    public Object readWithCache(String json, String path) {
        return JsonPath.using(Configuration.defaultConfiguration())
            .parse(json)
            .read(path);
    }

    // 预编译常用的JSONPath表达式(避免重复编译,提升性能)
    private final JsonPath compiledPath = JsonPath.compile("$.store.book[*]");

    /**
     * 使用预编译路径查询所有书籍
     * @param json 原始JSON字符串
     * @return 书籍列表
     */
    public List<Map<String, Object>> readOptimized(String json) {
        return compiledPath.read(json);
    }
}

对接外部 API 并提取数据:

复制代码
import com.jayway.jsonpath.JsonPath;
import org.springframework.web.client.RestTemplate;
import java.util.List;

public class ExternalApiService {

    private final RestTemplate restTemplate;

    // 构造器注入RestTemplate
    public ExternalApiService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * 调用外部API并通过JSONPath提取数据
     * @param url 外部API地址
     * @param jsonPath JSONPath表达式
     * @return 提取的数据列表
     */
    public List<Object> extractFromExternalApi(String url, String jsonPath) {
        // 调用外部API获取JSON响应
        String response = restTemplate.getForObject(url, String.class);
        // 解析并提取指定路径的数据
        return JsonPath.parse(response).read(jsonPath);
    }
}

常用过滤表达式示例:

复制代码
// 1. 价格大于10的书籍
// $.store.book[?(@.price > 10)]

// 2. 分类为fiction的书籍
// $.store.book[?(@.category == 'fiction')]

// 3. 包含isbn字段的书籍
// $.store.book[?(@.isbn)]

// 4. 作者名称包含Melville的书籍(正则匹配)
// $.store.book[?(@.author =~ /.*Melville.*/)]

// 5. 价格低于10且分类为fiction的书籍
// $.store.book[?(@.price < 10 && @.category == 'fiction')]

除了 Jayway JsonPath,市面上主流的 JSON 处理库也都有各自的 JSONPath 或类似功能实现,下面给大家逐一介绍。

04

FastJSON - 内置原生 JSONPath 支持

FastJSON 作为国内常用的 JSON 解析库,内置了完善的 JSONPath 功能,使用起来非常便捷。

首先引入 FastJSON 依赖:

复制代码
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.53</version>
</dependency>

基础使用示例:

复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONPath;
import com.alibaba.fastjson2.JSONObject;
import java.util.List;

public class FastJsonPathExample {

    // 示例JSON字符串
    private String json = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}";

    public void testFastJsonPath() {
        // 将JSON字符串解析为JSONObject
        JSONObject object = JSON.parseObject(json);

        // 1. 提取所有书籍的作者
        List<String> authors = (List<String>) JSONPath.eval(object, "$.store.book[*].author");

        // 2. 提取第一本书的价格
        Double price = (Double) JSONPath.eval(object, "$.store.book[0].price");

        // 3. 过滤价格低于10的书籍
        List<JSONObject> books = (List<JSONObject>) JSONPath.eval(object, "$.store.book[?(@.price < 10)]");

        // 4. 获取书籍数组的长度
        Integer size = (Integer) JSONPath.eval(object, "$.store.book.size()");

        // 5. 提取包含isbn字段的书籍
        List<JSONObject> booksWithIsbn = (List<JSONObject>) JSONPath.eval(object, "$.store.book[?(@.isbn)]");
    }
}

FastJSON 提供了多种查询方式,适配不同的业务场景:

复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONPath;
import com.alibaba.fastjson2.JSONObject;
import java.util.List;

public class FastJsonPathQueryExample {

    // 解析后的JSONObject对象
    private JSONObject object = JSON.parseObject("{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}");

    public void testDifferentQueryMethods() {
        // 方式1:直接调用eval方法(最简洁)
        List<String> authors1 = (List<String>) JSONPath.eval(object, "$.store.book[*].author");

        // 方式2:先创建Path对象,再调用extract方法
        JSONPath path = JSONPath.of("$.store.book[*].author");
        List<String> authors2 = (List<String>) path.extract(object);

        // 方式3:预编译Path表达式(重复使用时推荐)
        JSONPath compiledPath = JSONPath.compile("$.store.book[*].author");
        List<String> authors3 = (List<String>) compiledPath.eval(object);

        // 修改指定路径的值
        JSONPath pricePath = JSONPath.of("$.store.book[0].price");
        pricePath.set(object, 88.88);

        // 判断指定路径是否存在
        boolean hasBook = JSONPath.contains(object, "$.store.book");
        boolean hasIsbn = JSONPath.contains(object, "$.store.book[2].isbn");

        // 获取数组长度(两种方式)
        Integer arraySize = (Integer) JSONPath.eval(object, "$.store.book.size()");
        JSONPath sizePath = JSONPath.of("$.store.book.size()");
        Integer size = (Integer) sizePath.eval(object);
    }
}

FastJSON 的 JSONPath 一大亮点是支持修改 JSON 数据:

复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONPath;
import com.alibaba.fastjson2.JSONObject;

public class FastJsonPathModifyExample {

    public void testJsonPathSet() {
        // 解析JSON字符串为可修改的JSONObject
        JSONObject object = JSON.parseObject("{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}");

        // 1. 修改第一本书的价格
        JSONPath.set(object, "$.store.book[0].price", 99.99);

        // 2. 修改自行车的颜色
        JSONPath.set(object, "$.store.bicycle.color", "blue");

        // 3. 批量修改所有书籍的价格
        JSONPath.set(object, "$.store.book[*].price", 15.88);

        // 4. 修改包含isbn字段的书籍分类
        JSONPath.set(object, "$.store.book[?(@.isbn)].category", "classic");

        // 5. 给第一本书新增出版社字段
        JSONPath.set(object, "$.store.book[0].publisher", "Tech Press");

        // 6. 向书籍数组中添加新书籍
        JSONArray bookArray = (JSONArray) JSONPath.eval(object, "$.store.book");
        bookArray.add(JSON.parseObject("{\"title\":\"New Book\",\"price\":9.99}"));

        // 7. 删除自行车节点
        JSONPath.remove(object, "$.store.bicycle");

        // 打印修改后的JSON(格式化输出)
        System.out.println(JSON.toJSONString(object, true));
    }
}

FastJSON 还支持常用的数组操作:

复制代码
// 获取数组长度
// Integer size = (Integer) JSONPath.eval(object, "$.store.book.size()");

// 获取数组第一个元素
// Object first = JSONPath.eval(object, "$.store.book.first()");

// 获取数组最后一个元素
// Object last = JSONPath.eval(object, "$.store.book.last()");

// 获取数组所有值的集合
// Collection<Object> values = (Collection<Object>) JSONPath.eval(object, "$.store.book.values()");

05

Jackson - JsonPointer / Jackson JsonPath

Jackson 是 Spring Boot 默认集成的 JSON 解析库,它原生支持 JsonPointer(遵循 RFC 6901 标准),但这并不是完整的 JSONPath 实现。

如果需要使用完整的 JSONPath 功能,可参考以下两种方式:

方式1:使用原生 JsonPointer(适合简单场景)

先引入 Jackson 核心依赖:

复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.18.2</version>
</dependency>

使用示例:

复制代码
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonJsonPointerExample {

    // 示例JSON字符串
    private String json = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}";
    // Jackson核心解析器
    private ObjectMapper mapper = new ObjectMapper();

    public void testJsonPointer() throws Exception {
        // 将JSON字符串解析为JsonNode(不可变节点)
        JsonNode root = mapper.readTree(json);

        // 1. 构建JsonPointer并提取第一本书的作者
        JsonPointer ptr = JsonPointer.compile("/store/book/0/author");
        JsonNode authorNode = root.at(ptr);
        String author = authorNode.asText();

        // 2. 直接通过路径字符串提取第二本书的标题
        String title = root.at("/store/book/1/title").asText();
        // 3. 提取自行车的价格
        Double price = root.at("/store/bicycle/price").asDouble();
    }
}

JsonPointer 局限性

  • • 语法简单,仅支持基础的路径访问,不支持通配符、过滤表达式;

  • • 无法一次性获取多个节点的值;

  • • 不支持数组切片操作;

方式2:结合 Jayway JsonPath(适合复杂场景)

依赖和之前一致,只需新增 Jackson 适配配置:

复制代码
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import java.util.List;

public class JacksonJsonPathExample {

    // 示例JSON字符串
    private String json = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}";

    // 构建适配Jackson的JSONPath配置
    private Configuration configuration = Configuration.builder()
        .jsonProvider(new JacksonJsonNodeJsonProvider())
        .mappingProvider(new JacksonMappingProvider())
        .build();

    public void testJacksonJsonPath() {
        // 提取所有书籍的作者(使用Jackson作为底层解析器)
        List<String> authors = JsonPath.using(configuration)
            .parse(json)
            .read("$.store.book[*].author");
    }
}

06

Gson - 无原生 JSONPath 支持

Gson 是 Google 推出的 JSON 解析库,它本身没有提供 JSONPath 相关功能,这也是 Gson 相比其他库的一个短板。

如果项目中已经使用了 Gson,建议搭配 Jayway JsonPath 来弥补这个不足。

首先引入依赖:

复制代码
<!-- Gson核心依赖 -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.11.0</version>
</dependency>
<!-- JSONPath依赖 -->
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.9.0</version>
</dependency>

使用示例:

复制代码
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.jayway.jsonpath.JsonPath;
import java.util.List;

public class GsonJsonPathExample {

    // Gson解析器实例
    private Gson gson = new Gson();
    // 示例JSON字符串
    private String json = "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}}}";

    public void testGsonWithJsonPath() {
        // 1. 使用JSONPath提取所有书籍的作者
        List<String> authors = JsonPath.parse(json)
            .read("$.store.book[*].author");

        // 2. 将提取的结果转换为Gson的JsonElement(适配Gson生态)
        JsonElement element = gson.toJsonTree(authors);
    }
}

07

三种方案对比

特性 FastJSON Jackson + JsonPointer Jayway JsonPath
JSONPath 支持 原生完整支持 仅JsonPointer(基础) 完整支持
过滤表达式 支持 不支持 支持
通配符 支持 不支持 支持
性能 优秀 优秀 良好
生态稳定性 曾出现安全漏洞 最稳定 社区活跃
Spring Boot 集成 需手动配置 默认集成 需添加依赖

08

选型建议

  • 已有 FastJSON 项目:直接使用 FastJSON 内置的 JSONPath 功能,无需额外引入依赖;

  • 使用 Jackson 的项目:简单的字段提取场景用原生 JsonPointer 即可,复杂的过滤、批量提取场景建议引入 Jayway JsonPath;

  • 使用 Gson 的项目:搭配 Jayway JsonPath 使用,弥补 Gson 无 JSONPath 的短板;

  • 新项目:推荐使用 Jackson + Jayway JsonPath 组合,兼顾生态稳定性和功能完整性;

JSONPath 是处理 JSON 数据的高效工具,通过简洁的路径表达式,能轻松实现复杂字段提取、条件过滤和动态查询。

在 Spring Boot 项目中集成 JSONPath,能大幅简化 JSON 处理代码,提升代码的可读性和可维护性,是处理复杂 JSON 结构、对接第三方 API 数据的优质技术方案。

相关推荐
p***19943 小时前
SpringBoot项目中替换指定版本的tomcat
spring boot·后端·tomcat
Coder_Boy_3 小时前
【Java核心】企业级高并发系统底层设计思想
java·前端·数据库·spring boot·高并发
springlustre3 小时前
20GB核心知识库无损大迁徙:纯内网环境下从 Confluence 9到 Wiki.js 的踩坑与实践
python·wikijs·wikijs迁移
我爱娃哈哈3 小时前
SpringBoot + Docker + Jenkins:一键构建、测试、部署流水线,DevOps 从入门到上手
spring boot·docker·jenkins
henry1010103 小时前
利用Python一键清理AWS EC2实例
python·云计算·aws
小鸡吃米…3 小时前
TensorFlow - 词嵌入
人工智能·python·tensorflow·neo4j
.小小陈.3 小时前
Python基础语法详解4:函数、列表与元组全解析
开发语言·c++·python·学习
Lun3866buzha3 小时前
【石油泄漏检测】YOLO13-C3k2-RFCBAMConv模型详解与应用
python