JsonPath - 根据表达式路径解析Json
JsonPath
在xml的使用过程中,对于xml的解析我们知道可以使用xpath的方式,随意的获取到我们想要的属性值。那么在使用json时,我们能不能实现同样的操作呢?
答案就是 json-path
基础介绍
跟 XPath 类似,JsonPath 通过路径来检索JSON,对语法格式如下
语法
符号 | 描述 |
---|---|
$ | 表示json的根节点,表示根节点下的所有数据 |
. | 表示子节点,如 $.store 表示根节点下的store节点下的所有数据 |
.. | 可实现递归搜索,如 $..title 表示搜索json中所有key为title属性的值 |
- | 可表示某一层节点,如 $.*.book 表示根节点下所有节点的book节点数据
@ | 在表达式中使用,表示当前节点对象
['',''] | 如 $..['author'] 表示所有节点中author节点的值
[,] | 如 $..['0'] 表示所有节点中下标为0的节点的值
[start:end] | 如 $..book[2] 取json中book数组的第3个值
[?()] | 过滤器表达式,表达式结果必须是boolean
过滤器表达式
通常的表达式格式为:[?(@.age > 18)]
操作符 | 描述 |
---|---|
== | 等于符号,但数字1不等于字符1(note that 1 is not equal to ‘1’) |
!= | 不等于符号 |
< | 小于符号 |
<= | 小于等于符号 |
| 大于符号
= | 大于等于符号
=~ | 判断是否符合正则表达式,例如[?(@.name =~ /foo.*?/i)]
in | 所属符号,例如[?(@.size in [‘S’, ‘M’])]
nin | 排除符号
size | size of left (array or string) should match right
empty | 判空符号
示例
{
"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
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
在线测试:http://jsonpath.herokuapp.com/?path=$.store.book%5B*%5D.author
JsonPath表达式 | 结果 |
---|---|
$.store.book[*].author | 获取json中store下book下的所有author值 |
$..author | 获取所有的 author 的值 |
$.store.book.* | 获取json中store下book下的所有值 |
$.store..price | 获取json中store下所有price的值 |
$..book[2] | 获取json中book数组的第3个值 |
$..book[-2] | 倒数的第二本书 |
$..book[0,1] | 前两本书 |
$..book[:2] | 从索引0(包括)到索引2(排除)的所有图书 |
$..book[1:2] | 从索引1(包括)到索引2(排除)的所有图书 |
$..book[-2:] | 获取json中book数组的最后两个值 |
$..book[2:] | 获取json中book数组的第3个到最后一个的区间值 |
$..book[?(@.title)] | 获取json中book数组中包含title的所有节点 |
$.store.book[?(@.price < 10)] | 获取json中book数组中price<10的所有值 |
$..book[?(@.price <= $['expensive'])] | 获取json中book数组中price<=$['expensive']结果的所有值 |
$..book[?(@.author =~ /.*REES/i)] | 获取json中book数组中的作者以REES结尾的所有值(REES不区分大小写) |
$..* | 逐层列出json中的所有值,层级由外到内 |
$..book.length() | 获取json中book数组的长度 |
使用
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
特别说明:下文中使用的 JSON_DATA 变量的值都为上面所示的json范例
使用静态方法直接读
List<String> authors = JsonPath.read(JSON_DATA, "$.store.book[*].author");
如果需要多次读,那么这种方法不够理想,因为每次都会重新解析一次json数据
一次解析,多次使用
我们可以先将json数据一次解析,然后多次使用,提升性能。json-path提供了如ReadContext ,DocumentContext等类,我们可以随意使用,其关系如下:
DocumentContext documentContext = JsonPath.parse(JSON_DATA);
// 或者
ReadContext ctx = JsonPath.parse(JSON_DATA);
List<String> author = ctx.read("$.store.book[?(@.isbn)].author");
类型转换
在java中使用JsonPath时,当我们知道我们读取过后的返回值是什么类型时,JsonPath会尝试将其转换为我们想要的类型
// 结果为 "Nigel Rees" ,如果我们强制转换为List那么会抛出 java.lang.ClassCastException 异常
List<String> list = JsonPath.parse(JSON_DATA).read("$.store.book[0].author")
// 正常
String author = JsonPath.parse(JSON_DATA).read("$.store.book[0].author")
我们在解析相应的json是可以设置解析过后的值自动转换为对应的类型的。默认情况下,MappingProvider SPI提供了一个简单的对象映射器。
String JSON_DATA = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(JSON_DATA).read("$['date_as_long']", Date.class);
// 2014-09-23 15:00:11
如果我们需要转换为更加具体的对象,如一个POJO等,就需要我们配置更加详细的json解析器JacksonMappingProvider 或 GsonMappingProvider
Book book = JsonPath.parse(JSON_DATA).read("$.store.book[0]", Book.class);
Configuration conf = Configuration.builder().mappingProvider(new JacksonMappingProvider()).build();
TypeRef<List<String>> typeRef = new TypeRef<List<String>>(){};
List<String> titles = JsonPath.using(conf).parse(JSON_DATA).read("$.store.book[*].title", typeRef);
过滤
根据路径过滤
List<Map<String, Object>> books = JsonPath.parse(JSON_DATA).read("$.store.book[?(@.price < 10)]");
// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
根据过滤器过滤
Filter cheapFictionFilter = Filter.filter(Criteria.where("category").is("fiction").and("price").lte(10D));
List<Map<String, Object>> books = JsonPath.parse(JSON_DATA).read("$.store.book[?]", cheapFictionFilter);
// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
// 使用and或者or连接多个条件
Filter fooOrBar = filter(
where("foo").exists(true)).or(where("bar").exists(true)
);
Filter fooAndBar = filter(
where("foo").exists(true)).and(where("bar").exists(true)
);
自定义过滤器
ReadContext reader = JsonPath.parse(JSON_DATA);
Predicate booksWithIsbn = new Predicate() {
@Override
public boolean apply(PredicateContext context) {
return context.item(Map.class).containsKey("isbn");
}
};
reader.read("$.store.book[?].isbn", List.class, booksWithIsbn);
// ["0-553-21311-3","0-395-19395-8"]
注意:在自定义过滤器中,context.item(Map.class) 这句话。这句中的Map.class是根据预定的结果类型定义的,如果返回的是String类型值,那就改为String.class
返回值
在JsonPath中,我们可以通过配置来指定本次读取时是返回相应的值,还是返回符合结果路径
Configuration configuration = Configuration.builder().options(Option.AS_PATH_LIST).build();
List<String> pathList = JsonPath.using(configuration).parse(JSON_DATA).read("$..author");
// ["$['store']['book'][0]['author']","$['store']['book'][1]['author']","$['store']['book'][2]['author']","$['store']['book'][3]['author']"]
Option.AS_PATH_LIST 表示返回路径,同时,该类还有其他几个参数:
- DEFAULT_PATH_LEAF_TO_NULL:对应路径的节点不存在时,返回null
[
{
"name" : "john",
"gender" : "male"
},
{
"name" : "ben"
}
]
Configuration conf = Configuration.defaultConfiguration();
// 正常
String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
// 异常 PathNotFoundException thrown
String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
Configuration conf2 = conf.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
// 正常
String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
// 正常 (返回 null)
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
- ALWAYS_RETURN_LIST:始终将结果包装在List中
Configuration conf = Configuration.defaultConfiguration();
// 正常
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
// 异常 PathNotFoundException thrown
List<String> genders1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
- SUPPRESS_EXCEPTIONS :确保不会从路径评估传播异常
如果选项ALWAYS_RETURN_LIST存在,将返回一个空列表
如果选项ALWAYS_RETURN_LIST不存在返回null
操作key
有时,我们解析一个json并不是为了将其解析出来,用于其他。而是我们需要将json解析出来,然后去修改或者删除其中的key。比如对一个json格式数据进行某些字段的脱敏处理。这是我们就需要用到其提供的 .set() 和 .put() 方法,同时还有 .delete()、.add() ...
这些方法的实现都在 JsonContext 中,其继承关系如图:
使用
要想对json进行增删改,我们首先要提高一个路径,用来让JsonPath可以找到对应的key或者是节点。
先解析
// 首先解析json为文档
DocumentContext documentContext = JsonPath.parse(JSON_DATA);
根据提供的路径直接修改
- 路径直接定义到具体的值
// 将所有书籍的作者修改为dimples
JsonPath p = JsonPath.compile("$.store.book[*].author");
documentContext.set(p, "dimples");
- 路径定义到节点
此时不能直接使用set()方法,因为此时返回的不是具体值的列表,而是所有book子节点的列表。此时使用put方法为佳,如下,就实现了与上面代码一样的效果
JsonPath p = JsonPath.compile("$.*.book");
documentContext.put(p, author, "dimples");
- 不替换所有key
在上面的两种写法中,我们会替换book节点下的所有节点的author的值,那么我们怎么实现根据我们的需要修改值呢?
- 过滤器表达式
// 修改 author 值为 Nigel Rees 的元素的值
JsonPath p = JsonPath.compile(StrUtil.format("$..[?(@.author == 'Nigel Rees')]");
documentContext.put(p, author, "dimples");
- 使用过滤器
DocumentContext documentContext = JsonPath.parse(DATA);
filter = Filter.filter(Criteria.where("author ").is("Nigel Rees"));
// 替换
documentContext.put("$..[?]","author","dimples",filter);
其中 ? 代表过滤器的占位符,如果没有 ? ,那么配置filter将无效。
也可以使用set方法,原理类似
根据值条件获取路径,然后修改
Configuration conf = Configuration.builder().options(Option.AS_PATH_LIST).build();
Filter filter = Filter.filter(Criteria.where("author").contains("Nigel Rees");
// 获取满足条件值的路径
List<String> path = JsonPath.using(conf).parse(JSON_DATA).read("$..[?]", filter);
// 替换 (此处举例,不做遍历,只获取第一个)
documentContext.set(path.get(0), "dimples");
也可以使用put方法,原理类似
参考资料
https://blog.csdn.net/weixin_42452045/article/details/92768660
https://blog.csdn.net/Dream_Weave/article/details/106421388
FastJson - json-path
alibaba-json-path 官方参考:https://github.com/alibaba/fastjson/wiki/JSONPath
JsonPath - 根据表达式路径解析Json的更多相关文章
- 使用json-path解析json
在我们的日常开发中,有时候需要从一个json字符串中获取一个值,或者从一段json字符串中获取到某些值,如果先使用Gson或Jackson转换成java对象在获取值,有些时候是很麻烦的,那么有没有一种 ...
- 使用jsonpath解析json内容
JsonPath提供的json解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的json内容.下面我把官网介绍的每个表达式用代码实现,可以更直观的知道该怎么用它. 一.首先需要 ...
- Python | JSON 数据解析(Json & JsonPath)
一.什么是JSON? JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMAScript (欧洲计算机协会制定的js规范)的一 ...
- JAVA JSON解析:类XPATH解析JSON
目前JAVA解析JSON的方式有很多种,json-lib啊,GJSON啊,等等都可以解析,但通常是将JSON转换为对象或者是LIST或者是MAP,对于我们测试人员来说,其实我们并不需要里面的全部信息, ...
- Kettle解析JSON错误,We MUST have the same number of values for all paths,We can not find and data with path [$.
最近公司要从聚石塔上抽取数据,其中有JSON格式数据,所以学习一下Kettle解析JSON,碰到小小问题,记录一下: (1) 2015/07/15 15:22:48 - trade_detail.0 ...
- eval解析JSON中的注意点
在JS中将JSON的字符串解析成JSON数据格式,一般有两种方式: 1.一种为使用eval()函数. 2. 使用Function对象来进行返回解析. 使用eval函数来解析,并且使用jquery ...
- C#解析json文件的方法
C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...
- 《项目经验》--通过js获取前台数据向一般处理程序传递Json数据,并解析Json数据,将前台传来的Json数据写入数据库表中
先看一下我要实现的功能界面: 这个界面的功能在图中已有展现,课程分配(教师教授哪门课程)在之前的页面中已做好.这个页面主要实现的是授课,即给老师教授的课程分配学生.此页面实现功能的步骤已在页面 ...
- 安卓解析json,使用BaseAdapter添加至ListView中,中间存储用JavaBean来实现
这是一个小练习,要求解析一个提供的json文件.并将其中的id,title值获取,以ListView形式展示出来.(开发工具是android studio) 下面开始: 首先我想到的是先把json文件 ...
- [转]javascript eval函数解析json数据时为什加上圆括号eval("("+data+")")
javascript eval函数解析json数据时为什么 加上圆括号?为什么要 eval这里要添加 “("("+data+")");//”呢? 原因在于: ...
随机推荐
- 【走进RDS】之SQL Server性能诊断案例分析
简介: 数据库性能诊断不仅对其数据库技能要求较高,而且需要大量的前期准备工作,如收集各种性能基线.性能指标和慢SQL日志等,尤其是面对多数据库性能调优时,往往事倍功半. 客户的困扰 前几天某程序员小王 ...
- 直播回顾 | 云原生混部系统 Koordinator 架构详解(附完整PPT)
简介: 近期,来自 Koordinator 社区的两位技术专家从项目的架构和特性出发,分享了 Koordinator 是如何应对混部场景下的挑战,特别是提升混部场景下工作负载的运行的效率和稳定性,以及 ...
- DataWorks 如何撑起阿里99%的数据开发?
阿里妹导读: DataWorks是阿里巴巴自主研发,支撑阿里巴巴经济体99%数据业务建设和治理,每天数万名数据开发和算法开发工程师在使用.从2010年起步到目前的版本,经历了多次技术变革和架构升级,也 ...
- 慢sql治理经典案例分享
简介:菜鸟供应链金融慢sql治理已经有一段时间,自己负责的应用持续很长时间没有慢sql告警,现阶段在推进组内其他成员治理应用慢sql.这里把治理过程中的一些实践拿出来分享下. 作者 | 如期 来 ...
- 如何玩转 WebGL 并行计算
简介: 如今在 Web 端使用 WebGL 进行高性能计算已有不少实践,例如在端智能领域中的 tensorflow.js,再比如可视化领域中的 Stardust.js. 作者 | 沧东 来源 | ...
- dotnet 在 UOS 国产系统上使用 Xamarin Forms 创建 xaml 界面的 GTK 应用
在前面几篇博客告诉大家如何部署 GTK 应用,此时的应用是特别弱的,大概只是到拖控件级.尽管和 WinForms 一样也能写出特别强大的应用,但是为了提升一点开发效率,咱开始使用 xaml 神器写界面 ...
- OLAP系列之分析型数据库clickhouse备份方式(五)
一.常见备份方式 1.1 备份方式 备份方式 特点 物理文件备份 对物理文件进行拷贝,备份期间禁止数据写入 dump数据导入导出 备份方式灵活,但备份速度慢 快照表备份 制作_bak表进行备份 FRE ...
- SpringMVC学习五(resultful风格/异常处理/注解)
resultful风格 异常处理 1.Restfule风格 Restfule风格是一种软件架构风格,而不是标准,只是提供了一种设计原则和约束条件.主要适用于客户端和服务器端交互的软件.是基于http协 ...
- linux开发vue项目,不能热更新?
只需要运行下面的命令即可: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo ...
- vue3的reactive对象赋值后失去响应式的问题
vue3种对象类型的响应式用reactive实现. 但是reactive对象在赋值后,因为变量代理函数变了,就失去了响应式功能了.示例如下: <template> <div> ...