找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2331

积分

0

好友

333

主题
发表于 19 小时前 | 查看: 3| 回复: 0

在日常的后端开发中,处理JSON数据是一项高频操作。当我们需要从一个嵌套较深或结构复杂的JSON文档中,精准地提取出某个特定字段时,如果只使用传统的JSON库(如Gson、Jackson)的API,代码往往会显得冗长且不易维护。

有没有更优雅、更直观的方式呢?今天我们就来聊聊 JSONPath。你可以把它理解为JSON世界的XPath,通过书写简洁的路径表达式,就能轻松定位和提取你想要的任何数据。

什么是JSONPath?

JSONPath是一种专门用于从JSON文档中查询和提取特定数据的微型语言。它的语法设计得非常直观,类似于我们访问JavaScript对象属性的方式,学习成本很低。

常用JSONPath语法速查

表达式 说明
$ 根节点
@ 当前节点(常用于过滤表达式)
.[] 子节点操作符
.. 递归下降,匹配任意深度的节点
* 通配符,匹配所有成员或数组元素
[] 下标运算符,用于访问数组元素
[start:end] 数组切片
[?()] 过滤表达式,用于条件筛选

在Spring Boot中集成JSONPath

1. 添加依赖

首先,在你的 pom.xml 文件中引入 Jayway JsonPath 的依赖。

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.9.0</version>
</dependency>

2. 基础使用示例

我们先定义一个经典的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.JsonPath;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.PathNotFoundException;

public class JsonPathExample {

    private String json = "..."; // 上述 JSON 字符串

    @Test
    public void testReadJson() {
        // 获取所有书籍的作者
        List<String> authors = JsonPath.parse(json)
            .read("$.store.book
  • .author");         // 获取第一本书的价格         Double price = JsonPath.parse(json)             .read("$.store.book[0].price");         // 获取所有价格低于10元的书籍         List<Map> cheapBooks = JsonPath.parse(json)             .read("$.store.book[?(@.price < 10)]");         // 获取最后一本书         Map lastBook = JsonPath.parse(json)             .read("$.store.book[-1]");     } }
  • 在Spring Boot的REST API中的应用

    在实际的Web开发中,JSONPath 能帮助我们快速处理来自请求体或外部API的复杂JSON响应。

    import org.springframework.web.bind.annotation.*;
    import com.jayway.jsonpath.JsonPath;
    
    @RestController
    @RequestMapping("/api")
    public class BookController {
    
        @PostMapping("/extract")
        public ResponseEntity<?> extractData(@RequestBody String jsonString) {
            try {
                // 提取所有书籍标题
                List<String> titles = JsonPath.parse(jsonString)
                    .read("$.store.book
  • .title");             // 提取价格区间内的书籍             List<Map> books = JsonPath.parse(jsonString)                 .read("$.store.book[?(@.price >= 8 && @.price <= 12)]");             return ResponseEntity.ok(Map.of(                 "titles", titles,                 "filteredBooks", books             ));         } catch (PathNotFoundException e) {             return ResponseEntity.badRequest()                 .body("JSON路径不存在: " + e.getMessage());         }     }     @GetMapping("/authors")     public ResponseEntity<?> getAuthors(@RequestParam String jsonData) {         List<String> authors = JsonPath.parse(jsonData)             .read("$.store.book
  • .author");         return ResponseEntity.ok(authors);     } }
  • 3. 高级用法与优化

    自定义配置

    你可以通过Configuration对象来定制JsonPath的行为,比如抑制异常、启用缓存等,以适应不同的场景需求。

    import com.jayway.jsonpath.Configuration;
    import com.jayway.jsonpath.Option;
    
    @Configuration
    public class JsonPathConfig {
    
        public Configuration jsonPathConfiguration() {
            return Configuration.builder()
                // 路径不存在时返回null,而不是抛出异常
                .options(Option.SUPPRESS_EXCEPTIONS)
                // 路径指向的叶子节点不存在时返回null
                .options(Option.DEFAULT_PATH_LEAF_TO_NULL)
                // 总是返回列表(即使是单值)
                .options(Option.ALWAYS_RETURN_LIST)
                // 启用解析缓存,提升重复解析性能
                .options(Option.CACHE)
                .build();
        }
    }

    预编译与缓存

    对于需要反复执行的JSONPath表达式,预编译可以显著提升性能。

    @Service
    public class JsonPathCacheService {
    
        // 预编译一个常用的路径,提升性能
        private final JsonPath compiledPath = JsonPath.compile("$.store.book
  • ");     public List<Map> readOptimized(String json) {         return compiledPath.read(json);     } }
  • 过滤表达式详解

    过滤表达式 [?()] 是JSONPath最强大的功能之一,支持各种逻辑判断。

    // 价格大于10的书籍
    $.store.book[?(@.price > 10)]
    
    // category 为 fiction 的书籍
    $.store.book[?(@.category == 'fiction')]
    
    // 包含 isbn 字段的书籍
    $.store.book[?(@.isbn)]
    
    // 使用正则表达式匹配作者名
    $.store.book[?(@.author =~ /.*Melville.*/)]
    
    // 多条件组合:价格低于10且分类为小说的书籍
    $.store.book[?(@.price < 10 && @.category == 'fiction')]

    除了 Jayway JsonPath,其他主流的JSON处理库也提供了类似的功能,下面我们来对比一下。

    FastJSON - 内置JSONPath支持

    FastJSON在其最新版本中直接内置了JSONPath功能,使用起来非常方便。

    添加依赖

    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.53</version>
    </dependency>

    使用示例

    FastJSON的JSONPath用法多样,并且支持数据的修改操作,这是一个非常实用的特性。

    import com.alibaba.fastjson2.JSON;
    import com.alibaba.fastjson2.JSONPath;
    import com.alibaba.fastjson2.JSONObject;
    
    public class FastJsonPathExample {
    
        private String json = "..."; // 同上 JSON 示例
    
        @Test
        public void testFastJsonPath() {
            JSONObject object = JSON.parseObject(json);
    
            // 方式一:使用静态方法eval(最常用)
            List<String> authors = (List<String>) JSONPath.eval(object, "$.store.book
  • .author");         // 方式二:预编译路径,性能更优         JSONPath path = JSONPath.of("$.store.book
  • .author");         List<String> authors2 = (List<String>) path.extract(object);         // 修改数据:将第一本书的价格改为99.99         JSONPath.set(object, "$.store.book[0].price", 99.99);         // 判断路径是否存在         boolean hasIsbn = JSONPath.contains(object, "$.store.book[2].isbn");     } }
  • Jackson - JsonPointer与扩展

    Jackson作为SpringBoot默认的JSON库,原生支持的是 JsonPointer (RFC 6901) 标准,其功能是JSONPath的一个子集。

    使用JsonPointer

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.18.2</version>
    </dependency>
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.core.JsonPointer;
    
    public class JacksonJsonPointerExample {
    
        private String json = "...";
        private ObjectMapper mapper = new ObjectMapper();
    
        @Test
        public void testJsonPointer() throws Exception {
            JsonNode root = mapper.readTree(json);
    
            // 使用JsonPointer定位节点
            JsonPointer ptr = JsonPointer.compile("/store/book/0/author");
            String author = root.at(ptr).asText();
    
            // 链式写法
            String title = root.at("/store/book/1/title").asText();
        }
    }

    JsonPointer的限制:语法简单,不支持通配符 *、过滤表达式 [?()] 和数组切片,无法一次性获取多个值。

    为Jackson集成完整JSONPath

    如果你需要在Jackson生态中使用完整的JSONPath功能,可以集成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;
    
    public class JacksonJsonPathExample {
    
        // 配置使用 Jackson 作为底层提供者
        private Configuration configuration = Configuration.builder()
            .jsonProvider(new JacksonJsonNodeJsonProvider())
            .mappingProvider(new JacksonMappingProvider())
            .build();
    
        @Test
        public void testJacksonJsonPath() {
            List<String> authors = JsonPath.using(configuration)
                .parse(json)
                .read("$.store.book
  • .author");     } }
  • Gson - 无原生JSONPath

    Gson本身不提供JSONPath支持。如果你正在使用Gson,建议直接引入Jayway JsonPath来处理复杂的查询需求,两者可以很好地协同工作。

    方案对比与选型建议

    特性 FastJSON Jackson + JsonPointer Jayway JsonPath
    JSONPath 支持 原生支持,功能完整 仅支持JsonPointer(功能子集) 完整支持
    过滤表达式 支持 不支持 支持
    通配符 支持 不支持 支持
    修改操作 支持(强大特性) 支持(需操作JsonNode) 仅读取
    性能 优秀 优秀 良好
    Spring Boot 集成 需手动添加依赖 默认集成 需手动添加依赖
    生态与安全 曾有安全漏洞历史,需关注版本 最稳定,社区信任度高 社区活跃

    选型建议

    • 已有FastJSON项目:直接使用其内置的JSONPath,特别是需要修改JSON数据时。
    • 使用Jackson的项目:简单路径定位用JsonPointer;需要复杂查询、过滤时,引入Jayway JsonPath扩展。
    • 使用Gson的项目:搭配Jayway JsonPath使用。
    • 新项目/Spring Boot项目:推荐使用 Jackson作为主力库,在需要复杂查询时引入 Jayway JsonPath 的组合,在稳定性、性能和功能上取得平衡。

    总结

    JSONPath通过其声明式的路径表达式,极大地简化了从复杂JSON结构中抽取、过滤和查询数据的过程。在Spring Boot项目中,无论是处理前端请求、解析第三方API响应,还是进行内部数据转换,合理利用JSONPath都能让代码更简洁、意图更清晰。

    掌握JSONPath及其在不同JSON库中的使用方式,能让你在处理JSON数据时更加游刃有余。希望本文的梳理和对比,能帮助你在实际项目中做出更合适的技术选型。

    如果你想了解更多关于Java后端开发、系统架构设计的实战技巧,欢迎持续关注云栈社区,与众多开发者一起交流成长。




    上一篇:7大核心数据结构详解:从数组到哈希表,构建编程世界的基础
    下一篇:Spring事务管理解析:@Transactional陷阱与编程式事务实践
    您需要登录后才可以回帖 登录 | 立即注册

    手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

    GMT+8, 2026-1-16 20:42 , Processed in 0.226089 second(s), 40 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2025-2026 云栈社区.

    快速回复 返回顶部 返回列表