前言
在日常开发中,我们经常需要处理 json 数据,特别是从复杂的 json 结构中提取特定字段。传统的处理方式如 gson、jackson 的 api 虽然功能强大,但在处理复杂路径提取时代码往往显得冗长且不易维护。
今天给大家介绍一个更优雅的解决方案 —— jsonpath,它就像 json 界的 xpath,让我们可以用简洁的路径表达式来定位和提取 json 数据。
什么是 jsonpath
jsonpath 是一种用于从 json 文档中提取特定数据的查询语言。它的语法简洁直观,类似于 javascript 对象属性的访问方式。
常用 jsonpath 语法
| 表达式 | 说明 |
|---|---|
| $ | 根节点 |
| @ | 当前节点 |
| . 或 [] | 子节点操作符 |
| .. | 递归下降(任意深度) |
| * | 通配符,匹配所有成员/元素 |
| [] | 下标运算符 |
| [start:end] | 数组切片 |
| [?()] | 过滤表达式 |
spring boot 集成 jsonpath
1. 添加依赖
在 pom.xml 中添加 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 中的实际应用
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. 高级用法
自定义配置
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)
// 默认值为空集合
.options(option.default_path_leaf_to_null)
// 总是返回列表
.options(option.always_return_list)
// 缓存
.options(option.cache)
.build();
}
}
缓存解析结果
@service
public class jsonpathcacheservice {
private final map<string, object> cache = new concurrenthashmap<>();
public object readwithcache(string json, string path) {
return jsonpath.using(configuration.defaultconfiguration())
.parse(json)
.read(path);
}
// 预编译路径,提升性能
private final jsonpath compiledpath = jsonpath.compile("$.store.book[*]");
public list<map> readoptimized(string json) {
return compiledpath.read(json);
}
}
与 rest 调用结合
@service
public class externalapiservice {
private final resttemplate resttemplate;
public list<string> extractfromexternalapi(string url, string jsonpath) {
string response = resttemplate.getforobject(url, string.class);
return jsonpath.parse(response).read(jsonpath);
}
}
过滤表达式详解
// 价格大于10的书籍 $.store.book[?(@.price > 10)] // category 为 fiction 的书籍 $.store.book[?(@.category == 'fiction')] // 包含 isbn 字段的书籍 $.store.book[?(@.isbn)] // 正则匹配 $.store.book[?(@.author =~ /.*melville.*/)] // 多条件组合 $.store.book[?(@.price < 10 && @.category == 'fiction')]
除了 jayway jsonpath,常见的 json 处理库也有各自的 jsonpath 或类似功能实现。
fastjson - 内置 jsonpath
fastjson内置了 jsonpath 支持,使用起来非常简洁。
添加依赖
<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;
public class fastjsonpathexample {
private string json = "..."; // 同上 json 示例
@test
public void testfastjsonpath() {
jsonobject object = json.parseobject(json);
// 获取所有书籍作者
list<string> authors = (list<string>) jsonpath.eval(object, "$.store.book[*].author");
// 获取第一本书价格
double price = (double) jsonpath.eval(object, "$.store.book[0].price");
// 过滤价格小于10的书籍
list books = (list) jsonpath.eval(object, "$.store.book[?(@.price < 10)]");
// size 方法
integer size = (integer) jsonpath.eval(object, "$.store.book.size()");
// 获取所有包含 isbn 的书籍
list bookswithisbn = (list) jsonpath.eval(object, "$.store.book[?(@.isbn)]");
}
}
fastjson jsonpath 多种查询方式
fastjson 提供了多种查询方式,适应不同场景:
import com.alibaba.fastjson2.json;
import com.alibaba.fastjson2.jsonpath;
import com.alibaba.fastjson2.jsonobject;
public class fastjsonpathqueryexample {
private jsonobject object = json.parseobject(json);
@test
public void testdifferentquerymethods() {
// ========== 方式一:jsonpath.eval(静态方法,最常用)==========
list authors1 = (list) jsonpath.eval(object, "$.store.book[*].author");
// ========== 方式二:jsonpath.of + extract(推荐,性能更好)==========
// 预编译路径表达式,性能更优(适合重复使用)
jsonpath path = jsonpath.of("$.store.book[*].author");
list authors2 = (list) path.extract(object);
// ========== 方式三:compile + eval(另一种编译方式)==========
jsonpath compiledpath = jsonpath.compile("$.store.book[*].author");
list authors3 = (list) compiledpath.eval(object);
// ========== 方式四:路径对象直接调用 set(修改操作)==========
jsonpath pricepath = jsonpath.of("$.store.book[0].price");
pricepath.set(object, 88.88);
// ========== 方式五:contains(判断是否包含路径)==========
boolean hasbook = jsonpath.contains(object, "$.store.book");
boolean hasisbn = jsonpath.contains(object, "$.store.book[2].isbn");
// ========== 方式六:size(获取数组大小)==========
integer arraysize = (integer) jsonpath.eval(object, "$.store.book.size()");
// 或者使用编译后的路径
jsonpath sizepath = jsonpath.of("$.store.book.size()");
integer size = (integer) sizepath.eval(object);
}
}
fastjson jsonpath 修改操作
fastjson 的 jsonpath 不仅可以读取数据,还支持修改数据,这是它的一个强大特性。
import com.alibaba.fastjson2.json;
import com.alibaba.fastjson2.jsonpath;
import com.alibaba.fastjson2.jsonobject;
public class fastjsonpathmodifyexample {
@test
public void testjsonpathset() {
jsonobject object = json.parseobject(json);
// 修改第一本书的价格
jsonpath.set(object, "$.store.book[0].price", 99.99);
// 修改自行车的颜色
jsonpath.set(object, "$.store.bicycle.color", "blue");
// 批量修改所有书籍价格
jsonpath.set(object, "$.store.book[*].price", 15.88);
// 修改包含 isbn 的书籍的 category
jsonpath.set(object, "$.store.book[?(@.isbn)].category", "classic");
// 添加新字段
jsonpath.set(object, "$.store.book[0].publisher", "tech press");
// 数组末尾添加元素(通过路径获取数组后操作)
jsonarray bookarray = (jsonarray) jsonpath.eval(object, "$.store.book");
bookarray.add(json.parseobject("{\"title\":\"new book\",\"price\":9.99}"));
// 删除字段
jsonpath.remove(object, "$.store.bicycle");
system.out.println(json.tojsonstring(object));
}
}
fastjson jsonpath 其他操作
// 获取集合大小 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 values = (collection) jsonpath.eval(object, "$.store.book.values()");
jackson - jsonpointer / jackson jsonpath
jackson 原生支持 jsonpointer (rfc 6901),但不是完整的 jsonpath 实现。若要使用 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");
jsonnode authornode = root.at(ptr);
string author = authornode.astext();
// 链式写法
string title = root.at("/store/book/1/title").astext();
double price = root.at("/store/bicycle/price").asdouble();
}
}
jsonpointer 限制:
- 语法较简单,不支持通配符、过滤表达式
- 无法一次获取多个值
- 不支持数组切片
方式二:使用 jackson-jsonpath(第三方扩展)
<dependency>
<groupid>com.jayway.jsonpath</groupid>
<artifactid>json-path</artifactid>
<version>2.9.0</version>
</dependency>
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 使用。
<dependency>
<groupid>com.google.code.gson</groupid>
<artifactid>gson</artifactid>
<version>2.11.0</version>
</dependency>
<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;
public class gsonjsonpathexample {
private gson gson = new gson();
private string json = "...";
@test
public void testgsonwithjsonpath() {
// 使用 jsonpath 提取数据
list<string> authors = jsonpath.parse(json)
.read("$.store.book[*].author");
// 将结果转回 gson 对象
jsonelement element = gson.tojsontree(authors);
}
}
三种方案对比
| 特性 | fastjson | jackson + jsonpointer | jayway jsonpath |
|---|---|---|---|
| jsonpath 支持 | 原生支持 | 仅 jsonpointer | 完整支持 |
| 过滤表达式 | 支持 | 不支持 | 支持 |
| 通配符 | 支持 | 不支持 | 支持 |
| 性能 | 优秀 | 优秀 | 良好 |
| 生态稳定性 | 曾有安全漏洞 | 最稳定 | 社区活跃 |
| spring boot 集成 | 需手动配置 | 默认集成 | 需添加依赖 |
选型建议
- 已有 fastjson 项目:直接使用 fastjson 的 jsonpath
- 使用 jackson 的项目:简单场景用 jsonpointer,复杂场景引入 jayway jsonpath
- 使用 gson 的项目:建议搭配 jayway jsonpath 使用
- 新项目:推荐 jackson + jayway jsonpath 组合
总结
jsonpath 是处理 json 数据的利器,通过简洁的路径表达式实现复杂字段提取、条件过滤和动态查询。在 spring boot 中集成 jsonpath 可大幅简化代码、提升可读性,是处理复杂 json 结构和第三方 api 数据的一种可选技术方案。
以上就是springboot使用jsonpath实现高效处理json数据的详细内容,更多关于springboot处理json数据的资料请关注代码网其它相关文章!
发表评论