目录
前言
日常在书写用例断言的时候,经常会遇到这样的场景:从结果中提取关键属性用于后续业务或者断言。
一般遇到这类情况,处理方式基本都跟剥洋葱一样,遇到数组/集合,一层层循环读取,遇到对象套对象,一层层对象点属性点出来。对于要从复杂的结果里面提取关键数据,要写的解析代码很多,有没有办法精简这种提取操作呢?
群众的智慧是强大的,所以结论肯定是有的,而且已经出现很久了,它就是 jmeter 自带的后置处理器之JSON提取器里使用到的库:JSONPath
JSONPath介绍
JSONPath是由Stefan Goessner在2007年左右提出的概念,它是受到XPath(XML路径语言)的启发,设计用于从JSON文档中抽取或筛选指定数据的一种表达式语言。类似于XPath作用于XML文档,JSONPath允许开发者通过特定路径语法来选取JSON对象中的特定部分。
它解决了哪些问题呢?
-
零脚本编写,可直接在客户端完成从 JSON 结构中查找和提取数据
-
减少和服务端的交互,减少服务器响应带宽的使用
随着语言的发展和JSON 使用场景的不断丰富,目前 JSONPath 已经支持了很多主流语言,如java,js,groovy,php,go 等;
下面以 java 语言为例,举例说明。
操作项
操作项 | 描述 |
---|---|
$ | 根元素,所以表达式的开头 |
@ | 筛选器谓词正在处理的当前节点 |
* | 通配符。在需要名称或数字的任何地方可用 |
.. | 深度扫描。在需要名称的任何地方都可用 |
. | 点符号子项,如 $.name |
['' (, '')] | 括号内标注的一个或多个子项 |
[(,)] | 数组索引或索引 |
[start:end] | 数组切片运算符 |
[?()] | 筛选表达式。表达式的计算结果必须为布尔值 |
筛选器运算符
运算符 | 描述 |
---|---|
== | left 等于 right(请注意,1 不等于“1”) |
!= | left 不等于 right |
< | 左边小于右边 |
<= | left 小于或等于 right |
> | left 大于 right |
>= | left 大于或等于 right |
=~ | left 匹配正则表达式 [?(@.name =~ /foo.*?/i)] |
in | left 存在于右边 [?(@.size 在 ['S', 'M'])] 中 |
nin | left 在 right 中不存在 |
subsetof | left 是 right [?(@.sizes 子集 ['S', 'M', 'L'])] |
anyof | left 与 right 有交点 [?(@.sizes anyof ['M', 'L'])] |
noneof | 左边与右边没有交点 [?(@.sizes noneof ['M', 'L'])] |
size | left 的大小(数组或字符串)应与 right 匹配 |
empty | left(数组或字符串)应为空 |
函数
运算符 | 输出类型 |
---|---|
min() | Double |
max() | Double |
avg() | Double |
stddev() | Double |
length() | Integer |
sum() | Double |
keys() | Set |
concat(X) | like input |
append(X) | like input |
first() | Depends on the array |
last() | Depends on the array |
index(X) | Depends on the array |
样本
{ "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 | 返回书店中所有图书的作者 |
$..author | 所有作者 |
$.store.* | 返回书店中所有的东西,不仅包括书,还有其他物品,比如自行车 |
$.store..price | 返回书店中所有物品的价格 |
$..book[2] | 第三本图书 |
$..book[-2] | 倒数第二本书 |
$..book[0,1] | 前两本书 |
$..book[:2] | 从索引 0(含)到索引 2(不含)的所有图书 |
$..book[1:2] | 从索引 1(含)到索引 2(不含)的所有图书 |
$..book[-2:] | 最后两本书 |
$..book[2:] | 从索引 2(含)到最后的所有书籍 |
$..book[?(@.isbn)] | 所有带有 ISBN 编号的图书 |
$.store.book[?(@.price == 10)] | 返回店内价格等于10元的所有图书 |
$.store.book[?(@.price < 10)] | 返回店内价格小于10元的所有图书 |
$..book[?(@.price <= 10)] | 返回店内价格小于10元的所有图书 |
$..book[?(@.author =~ /.*REES/i)] | 所有与正则表达式匹配的书籍(忽略大小写) |
$..* | 把一切东西都返回 |
$..book.length() | 书籍数量 |
还有一种很奇怪的用法:$..book[?(@.price < $['expensive'])]
返回店内所有不“贵”的书。这里的 $['expensive']
是获取这个属性对应的值,即$..book[?(@.price<10)]
。
基础语法到此已介绍完毕,快去实践起来吧。
延伸
-
体验工具,网页搜索JSONPATH在线验证,注意有些工具的语法会有些许出入
-
如何引入工程里面简化取数逻辑代码呢?通过pom导入jar包,注意jar的版本,不同的版本对应的服务是存在差异的
-
也可在自己的工具里面,提供书写表达式窗口来简化自己的工具