文章目录
- 一、快速入门
- 1.1 Json Path 介绍
- 1.2 引入依赖
- 二、基本使用
- 2.1 基本用法
- 2.2 基本语法
- 2.3 函数语法
- 2.4 筛选语法
- 三、高级用法
- 3.1 动态筛选
- 3.2 多条件筛选
- 3.3 嵌套查询
- 四、API
- 4.1 示例
- 2.2 常用方法
- 2.3 类型转换
- 四、小结
一、快速入门
1.1 Json Path 介绍
正如XPath对XML的解析一样,JSONPath的定义是基于fastjson的json路径解析,对JSON文档的一种解析工具。通过JSONPath可以轻松的对JSON文档获取指定“路径”的数据,在非常复杂的json结构中,对于一些获取和判断操作,不需要层层的去get,可以通过简洁的JsonPath表达式精准找到需要的部分。
1.2 引入依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>x.y.z</version> </dependency>
复制
- JSONPath在线验证:https://www.jsonpath.cn/
二、基本使用
2.1 基本用法
根据需要解析的路径,使用JsonPath类的静态方法read解析json字符串,并使用read方法返回的Object进行数据获取
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; System.out.println("所有author节点:" + JSONPath.read(jsonStr, "$..author"));
复制
如果是json格式的字符串,则先解析为JSONObject,然后就能直接使用JSONPath.eval了。
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; // 先解析JSON数据 JSONObject jsonObject = JSON.parseObject(jsonStr); System.out.println("\n Book数目:" + JSONPath.eval(jsonObject, "$.store.book.size()")); System.out.println("第一本书title:" + JSONPath.eval(jsonObject, "$.store.book[0].title")); System.out.println("price大于10元的book:" + JSONPath.eval(jsonObject, "$.store.book[price > 10]")); System.out.println("price大于10元的title:" + JSONPath.eval(jsonObject, "$.store.book[price > 10][0].title")); System.out.println("bicycle的所有属性值" + JSONPath.eval(jsonObject, "$.store.bicycle.*")); System.out.println("bicycle的color和price属性值" + JSONPath.eval(jsonObject, "$.store.bicycle['color','price']"));
复制
2.2 基本语法
Json Path的语法相对简单,它采用开发语言友好的表达式形式,如下所示。
JSONPath | 描述 |
---|---|
$ | 文档的根对象,所有路径表达式的开始 |
@ | 文档当前节点对象,类似于java 中的 this 字段 |
. | 子节点,例如$.name |
[num] | 数组访问,其余num 是数字,可以是负数。例如$[0].leader.departments[-1].name |
[‘key’] | 属性访问。例如$[‘name’] |
[‘key0’,‘key1’] | 多个属性访问。例如$[‘id’,‘name’] |
[num0,num1,num2] | 数组多个元素访问,其余num 是数字,可以是负数,返回数组中的多个元素。 例如$[0,1,-2,5] |
… | 可以理解为递归搜索,递归匹配所有子元素。例如$…name |
* | 通配符,适用于任何地方,例如$.leader.* |
[start:end] | 数组范围访问,其中start、end 是开始、结束的下标,可以是负数,返回数组中的多个元素。 |
[?(expression)] | 过滤器表达式,必须返回一个布尔值,例如$.departs[?(name)] |
() | 脚本表达式 |
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; System.out.println("根节点:" + JSONPath.read(jsonStr, "$")); System.out.println("根节点下的store节点:" + JSONPath.read(jsonStr, "$.store")); System.out.println("store节点下的所有子节点:" + JSONPath.read(jsonStr, "$.store.*")); System.out.println("第一个book节点:" + JSONPath.read(jsonStr, "$.store..book[0]"));
复制
JSONPath | 语义 |
---|---|
$ | 根对象 |
$[-1] | 最后元素 |
$[:-2] | 第1个至倒数第2个 |
$[1:] | 第2个之后所有元素 |
$[1,2,3] | 集合中1、2、3个元素 |
2.3 函数语法
可以在JsonPath表达式执行后进行调用,其输入值为表达式的结果。
函数 | 描述 | 输出类型 |
---|---|---|
min() | 数字数组的最小值 | Double |
max() | 数字数组的最大值 | Double |
avg() | 数字数组的平均值 | Double |
stddev() | 数字数组的标准方差 | Double |
length() | 返回数组的长度 | Integer |
sum() | 数字数组求和 | Double |
keys() | 没搞明白这个函数是做什么的 | Set |
concat(X) | 将数组中元素拼接成一个新的元素 | |
append(X) | 添加新元素到输出数组中 |
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; System.out.println("第一个book节点的价格,并向下取整:" + JSONPath.read(jsonStr, "$.store.book[0].price.floor()"));
复制
2.4 筛选语法
筛选语法用于筛选满足特定条件的节点,是用于过滤数组的逻辑表达式,一个通常的表达式形如:[?(@.age > 18)],可以通过逻辑表达式&&或||组合多个过滤器表达式,例如[?(@.price < 10 && @.category == ‘fiction’)],字符串必须用单引号包围,例如[?(@.color == ‘blue’)]。
操作符 | 描述 |
---|---|
== | 字符串类型对象属性比较过滤。例如$.departs[name = ‘123’],比较操作符支持=、!=、>、>=、<、<= |
> | 数值类型对象属性比较过滤,例如$.departs[id >= 123],比较操作符支持=、!=、>、>=、<、<= |
=~ | 判断是否符合正则表达式 |
like | 字符串类型 like 过滤,例如$.departs[name like ‘sz*’],通配符只支持%,也支持 not like |
rlike | 字符串类型正则匹配过滤,例如departs[name like ‘aa(.)*’],正则语法为jdk的正则语法,支持not rlike |
in | IN 过滤,支持字符串、数值类型。例如: $.departs[name in (‘wenshao’,‘Yako’)] $.departs[id not in (101,102)] |
nin | 排除 |
between | BETWEEN过滤, 支持数值类型,支持not between。例如: $.departs[id between 101 and 201] $.departs[id not between 101 and 201] |
empty | 左边数组或字符串为空 |
size | 左边数组或字符串大小和右边匹配 |
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; System.out.println("选取store节点下,价格小于10的book节点:" + JSONPath.read(jsonStr, "$.store.book[?(@.price < 10)]")); System.out.println("选取store节点下,价格小于10的book节点:" + JSONPath.eval(jsonStr, "$.store.book[?(@.price < 10)]"));
复制
三、高级用法
JsonPath支持许多高级用法,如动态筛选、多条件筛选、嵌套查询等。
3.1 动态筛选
JsonPath支持使用Java代码动态生成筛选条件。
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; //定义筛选条件 String filter = "$.store.book[?(@.price < %s)].price"; //动态生成筛选条件,获取价格小于10的book节点的价格 Object result = JSONPath.read(jsonStr, String.format(filter, 10)); System.out.println(result.toString());
复制
3.2 多条件筛选
JsonPath支持同时使用多个条件进行筛选。
String jsonStr = "{'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}]," + "'bicycle':{'color':'red','price':19.95}}}\n"; //定义筛选条件 String filter = "$.store.book[?(@.price < %s && @.author == '%s')].title"; //动态生成筛选条件,获取价格小于10且作者是Nigel Rees的book节点的书名 Object result = JSONPath.read(jsonStr, String.format(filter, 10, "Nigel Rees")); System.out.println(result.toString());
复制
3.3 嵌套查询
JsonPath支持在筛选条件中使用嵌套查询。
复制
四、API
4.1 示例
{ "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 }
复制
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数组的长度 |
2.2 常用方法
使用Fastjson中的JSONPath解析器,可以调用 JSONPath.read 方法,获取指定的字符串。
List<String> authors = JsonPath.read(JSON_DATA, "$.store.book[*].author");
复制
如果需要多次读,那么这种方法不够理想,因为每次都会重新解析一次json数据
可以看到,在这段难以阅读的json字符串中,我们轻松取到了 author 的值。这只是read方法的用法,下面介绍一下这个类其他的几个关键方法的作用:
public class JSONPath { // 求值,静态方法 public static Object eval(Object rootObject, String path); // 计算Size,Map非空元素个数,对象非空元素个数,Collection的Size,数组的长度。其他无法求值返回-1 public static int size(Object rootObject, String path); // 是否包含path中是否存在对象 public static boolean contains(Object rootObject, String path); // 是否包含path中是否存在指定值,如果是集合或者数组,在集合中查找value是否存在 public static boolean containsValue(Object rootObject, String path, Object value); // 修改制定路径的值,如果修改成功,返回true,否则返回false public static boolean set(Object rootObject, String path, Object value); // 在数组或者集合中添加元素。添加成功返回 true,失败返回 false public static boolean arrayAdd(Object rootObject, String path, Object… values); // 删除指定path的元素, 删除成功返回 true,失败返回 false public static boolean remove(Object root, String path); // 编译一个jsonpath为对象 public static JSONPath compile(String path); // 从一个json字符串中, 根据指定的path读取为Json对象 public static Object read(String json, String path); // 返回指定Java对象的属性的所有json访问path public static Map<String, Object> paths(Object javaObject); }
复制
2.3 类型转换
在java中使用JsonPath时,当我们知道我们读取过后的返回值是什么类型时,JsonPath会尝试将其转换为我们想要的类型,如下所示:
String author = (String) JSONPath.read(JSON_DATA, "$.store.book[0].author"); System.out.println(author);
复制
四、小结
使用JSONPath类提供的静态方法能够非常快速地对json字符串进行路径解析,将指定位置的json字符串以对象的形式返回,方便了系统的调用者。JSONPath与XPath的路径解析规则差不多,熟练使用这种解析方式,再用上Fastjson提供的静态方法,可以将在需要使用JSONPath的项目中将解析速度大大提升。