基于json解析神器 jsonpath的使用说明
如果项目需求是从某些复杂的json里面取值进行计算,用jsonpath+IK(ik-expression)来处理十分方便,jsonpath用来取json里面的值然后用IK自带的函数进行计算,如果是特殊的计算那就自定义IK方法搞定,配置化很方便.
下面简单介绍下jsonpath的使用方法,主要测试都在JsonPathDemo类里面:
下面是一个简单的java项目demo:
注意:其中他的max,min,avg,stddev函数只能类似于如下处理:
//正确写法但是感觉很鸡肋
context.read("$.avg($.result.records[0].loan_type,$.result.records[1].loan_type,$.result.records[2].loan_type)");
不能传入list感觉比较鸡肋,如果传入list他会报错(如下错误写法):
//这样会报错
ObjectmaxV=context.read("$.max($.result.records[*].loan_type)");
//这样也会报错
ObjectmaxV=context.read("$.result.records[*].loan_type.max()");
//如果json文件中是这样:"loan_type":"2",也会报错,"loan_type":2这样才被认为是数字
报错信息都一样,如下:
Exceptioninthread"main"com.jayway.jsonpath.JsonPathException:Aggregationfunctionattemptedtocalculatevalueusingemptyarray
JsonPathDemo是一个测试demo:
publicclassJsonPathDemo{
publicstaticvoidmain(String[]args){
Stringjson=FileUtils.readFileByLines("demo.json");
ReadContextcontext=JsonPath.parse(json);
//1返回所有name
Listnames=context.read("$.result.records[*].name");
//["张三","李四","王五"]
System.out.println(names);
//2返回所有数组的值
List
pom文件引入:
com.jayway.jsonpath json-path 2.3.0
其中demo.json是一个测试json:
{
"action":"/interface.service/xxx/queryBlackUserData",
"all":"1",
"result":{
"count":2,
"tenant_count":2,
"records":[
{
"name":"张三",
"pid":"500234199212121212",
"mobile":"18623456789",
"applied_at":"3",
"confirmed_at":"5",
"confirm_type":"overdue",
"loan_type":1,
"test":"mytest",
"all":"2"
},
{
"name":"李四",
"pid":"500234199299999999",
"mobile":"13098765432",
"applied_at":"1",
"confirmed_at":"",
"confirm_type":"overdue",
"loan_type":3,
"all":"3"
},
{
"name":"王五",
"pid":"50023415464654659",
"mobile":"1706454894",
"applied_at":"-1",
"confirmed_at":"",
"confirm_type":"overdue",
"loan_type":3
}
],
"all":"4"
},
"code":200,
"subtime":"1480495123550",
"status":"success",
"ok":3
}
FileUtils类是用于读取xx.json文件为字符串的json:
publicclassFileUtils{
/**
*以行为单位读取文件,常用于读面向行的格式化文件
*/
publicstaticStringreadFileByLines(StringfileName){
Filefile=newFile(fileName);
BufferedReaderreader=null;
Stringstr="";
try{
InputStreamis=FileUtils.class.getClassLoader().getResourceAsStream(fileName);
reader=newBufferedReader(newInputStreamReader(is));
StringtempString=null;
intline=1;
//一次读入一行,直到读入null为文件结束
while((tempString=reader.readLine())!=null){
//显示行号
str+=tempString;
}
reader.close();
}catch(IOExceptione){
e.printStackTrace();
}finally{
if(reader!=null){
try{
reader.close();
}catch(IOExceptione1){
}
}
}
returnstr;
}
}
补充:json接口测试的利器jsonpath
在测试REST接口的时候,经常要解析JSON,那么可以使用开源jsonpath进行,其中看网上看到相关的说法不错的使用场景为:
1、接口关联
也称为关联参数。在应用业务接口中,完成一个业务功能时,有时候一个接口可能不满足业务的整个流程逻辑,需要多个接口配合使用,简单的案例如:B接口的成功调用依赖于A接口,需要在A接口的响应数据(response)中拿到需要的字段,在调用B接口的时候,传递给B接口作为B接口请求参数,拿到后续响应的响应数据。
接口关联通常可以使用正则表达式去提取需要的数据,但对于json这种简洁、清晰层次结构、轻量级的数据交互格式,使用正则未免有点杀鸡用牛刀的感觉(是的,因为我不擅长写正则表达式),我们需要更加简单、直接的提取json数据的方式。
2、数据验证
这里的数据验证指的是对响应结果进行数据的校验
接口自动化测试中,对于简单的响应结果(json),可以直接和期望结果进行比对,判断是否完全相等即可。
如json{"status":1,"msg":"登录成功"}
3、对于格式较复杂
尤其部分数据存在不确定性、会根据实际情况变化的响应结果,简单的判断是否完全相等(断言)通常会失败。
如:
json{"status":1,"code":"10001","data":[{"id":1,"investId":"1","createTime":"2018-04-2712:24:01","terms":"1","unfinishedInterest":"1.0","unfinishedPrincipal":"0","repaymentDate":"2018-05-2712:24:01","actualRepaymentDate":null,"status":"0"},{"id":2,"investId":"1","createTime":"2018-04-2712:24:01","terms":"2","unfinishedInterest":"1.0","unfinishedPrincipal":"0","repaymentDate":"2018-06-2712:24:01","actualRepaymentDate":null,"status":"0"},{"id":3,"investId":"1","createTime":"2018-04-2712:24:01","terms":"3","unfinishedInterest":"1.0","unfinishedPrincipal":"100.00","repaymentDate":"2018-07-2712:24:01","actualRepaymentDate":null,"status":"0"}],"msg":"获取信息成功"}
上面的json结构嵌套了很多信息,完整的匹配几乎不可能成功。比如其中的createTime信息,根据执行接口测试用例的时间每次都不一样。同时这个时间是响应结果中较为次要的信息,在进行接口自动化测试时,是可以选择被忽略的。
4、我们需要某种简单的方法
能够从json中提取出我们真正关注的信息(通常也被称为关键信息)。
如提取出status的值为1,data数组中每个对象的investId都为1,data中第三个对象的unfinishedPrincipal值为100.00,只要这三个关键信息校验通过,我们就认为响应结果没有问题。
JSONPATH有点像XPATH了,语法规则小结下:
这里有个表格,说明JSONPath语法元素和对应XPath元素的对比。
| XPath | JSONPath | Description |
| / | $ | 表示根元素 |
| . | @ | 当前元素 |
| / | .or[] | 子元素 |
| .. | n/a | 父元素 |
| // | .. | 递归下降,JSONPath是从E4X借鉴的。 |
| * | * | 通配符,表示所有的元素 |
| @ | n/a | 属性访问字符 |
| [] | [] |
子元素操作符 |
| | | [,] |
连接操作符在XPath结果合并其它结点集合。JSONP允许name或者数组索引。 |
| n/a | [start:end:step] |
数组分割操作从ES4借鉴。 |
| [] | ?() |
应用过滤表示式 |
| n/a | () |
脚本表达式,使用在脚本引擎下面。 |
| () | n/a | Xpath分组 |
下面是一个简单的json数据结构代表一个书店(原始的xml文件是)
{"store":{
"book":[
{"category":"reference",
"author":"NigelRees",
"title":"SayingsoftheCentury",
"price":8.95
},
{"category":"fiction",
"author":"EvelynWaugh",
"title":"SwordofHonour",
"price":12.99
},
{"category":"fiction",
"author":"HermanMelville",
"title":"MobyDick",
"isbn":"0-553-21311-3",
"price":8.99
},
{"category":"fiction",
"author":"J.R.R.Tolkien",
"title":"TheLordoftheRings",
"isbn":"0-395-19395-8",
"price":22.99
}
],
"bicycle":{
"color":"red",
"price":19.95
}
}
}
| XPath | JSONPath | 结果 |
| /store/book/author | $.store.book[*].author |
书点所有书的作者 |
| //author | $..author |
所有的作者 |
| /store/* | $.store.* |
store的所有元素。所有的bookst和bicycle |
| /store//price | $.store..price |
store里面所有东西的price |
| //book[3] | $..book[2] |
第三个书 |
| //book[last()] | $..book[(@.length-1)] | 最后一本书 |
| //book[position()<3] | $..book[0,1]
$..book[:2] |
前面的两本书。 |
| //book[isbn] | $..book[?(@.isbn)] | 过滤出所有的包含isbn的书。 |
| //book[price<10] | $..book[?(@.price<10)] | 过滤出价格低于10的书。 |
| //* | $..* |
所有元素。 |
比如在单元测试MOCK中,就可以这样使用:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
publicclassBookControllerTest{
@Autowired
privateMockMvcmockMvc;
@MockBean
privateBookRepositorymockRepository;
/*
{
"timestamp":"2019-03-05T09:34:13.280+0000",
"status":400,
"errors":["Authorisnotallowed.","Pleaseprovideaprice","Pleaseprovideaauthor"]
}
*/
//article:jsonpathinarray
@Test
publicvoidsave_emptyAuthor_emptyPrice_400()throwsException{
StringbookInJson="{\"name\":\"ABC\"}";
mockMvc.perform(post("/books")
.content(bookInJson)
.header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.timestamp",is(notNullValue())))
.andExpect(jsonPath("$.status",is(400)))
.andExpect(jsonPath("$.errors").isArray())
.andExpect(jsonPath("$.errors",hasSize(3)))
.andExpect(jsonPath("$.errors",hasItem("Authorisnotallowed.")))
.andExpect(jsonPath("$.errors",hasItem("Pleaseprovideaauthor")))
.andExpect(jsonPath("$.errors",hasItem("Pleaseprovideaprice")));
verify(mockRepository,times(0)).save(any(Book.class));
}
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。