近期,为了给一个核心系统减负,组内决定将一些调用量大的查询接口迁移到另一个系统,由于接口逻辑比较复杂,为了保证接口逻辑一致,我们决定将一周内的请求参数在两个接口重放,并用脚本校验两边接口的响应结果。
接口返回数据是json结构的,本想用icdiff、jq来实现,但时间紧迫一时没想出来,同事用python脚本实现了这个对比,但后来又仔细想了想,找出了使用icdiff、jq实现的方法。
对于普通的文本对比,使用diff命令即可完成,但icdiff比diff更好用,对比结果也会更加直观。
$ text1='{"name":"apple","item_no":2}'
$ text2='{"name":"a apple","item_no":2}'
$ icdiff <(echo "$text1") <(echo "$text2")
但如果用jq格式化一下再对比,看起来会更加清晰一些。
$ icdiff <(echo "$text1"|jq .) <(echo "$text2"|jq .)
但是有时json数据可能会出现数据一样,但key的顺序不一致的问题,比如:
$ text1='{"name":"apple","item_no":2}'
$ text2='{"item_no":2,"name":"apple"}'
$ icdiff <(echo "$text1"|jq .) <(echo "$text2"|jq .)
像对象转json这种操作,是有可能出现不同机器上返回json的key顺序不同的。
好在jq提供了-S选项,可对输出json的key排序,如下:
$ icdiff <(echo "$text1"|jq -S .) <(echo "$text2"|jq -S .)
本以为这样就解决了所有问题,但在脚本运行过程中,我们又发现了新情况,json中的数组项,顺序可能不一致,如下:
$ text1='{
"order_id": 121345435624,
"waybills": [
{
"waybill_id": 1,
"name": "package1",
"items": [
{
"name": "orange",
"item_no": 1
},
{
"name": "apple",
"item_no": 2
}
]
},
{
"waybill_id": 2,
"name": "package2",
"items": [
{
"name": "pear",
"item_no": 3
},
{
"name": "banana",
"item_no": 4
}
]
}
]
}'
$ text2='{
"order_id": 121345435624,
"waybills": [
{
"waybill_id": 2,
"name": "package2",
"items": [
{
"name": "banana",
"item_no": 4
},
{
"name": "pear",
"item_no": 3
}
]
},
{
"waybill_id": 1,
"name": "package1",
"items": [
{
"name": "orange",
"item_no": 1
},
{
"name": "apple",
"item_no": 2
}
]
}
]
}'
$ icdiff <(echo "$text1"|jq -S .) <(echo "$text2"|jq -S .)
因为icdiff没法识别这种顺序区别,它只是逐行进行对比的,虽然两个json逻辑上是一样的,但它区别不出来。
程序中以下情况会造成这种顺序区别:
要解决这种情况还真挺麻烦的,因为需要对json中的数组进行排序,然后再进行对比,之前就是这里想不出来怎么实现。
但在后来,我又仔细的看了看jq的man文档,发现了一个有用的函数walk,然后就试了试,还真发现可以实现。
$ json_sort='walk(if type == "array" and length >0 then (if .[0].item_no then sort_by(.item_no) elif .[0].waybill_id then sort_by(.waybill_id) else. end) else . end)'
$ icdiff <(echo "$text1"|jq -S "$json_sort") <(echo "$text2"|jq -S "$json_sort")
Linux命令结合起来,总能产生更大的作用,当然这也需要付出一些折腾时间啊。