JMESPath 是 JSON 查询语言,可以从 JSON 文档中提取和转换元素。在做接口自动化测试项目时,最基础的一步就是从响应中获取各种待验证的字段值,掌握 jmespath 语法,能达到事半功倍的效果。
官方文档:
JMESPath — JMESPath
JMESPath Examples — JMESPath
安装
pip install jmespath
jmespath.py 库提供了两个接口:
函数说明:
- compile:与 re 模块类似,使用 compile 函数编译表达式,并使用解析后的表达式执行重复搜索
- search:接收表达式和数据,返回提取结果
1、获取键值对
示例代码:
import jmespath
data = {
"name": "张三",
"age": 26,
"gender": "男",
"grade": {
"Chinese": 96,
"Math": 99
},
"records": [
{
"Chinese": 95,
"Math": 100
},
{
"Chinese": 98,
"Math": 98
}
]
}
# 获取键值对值
search_cond = '{NAME:name, AGE:age, RECORDS:records}'
res = jmespath.search(search_cond, data)
print(res)
运行结果:
2、获取对象的值
示例代码:
import jmespath
data = {
"name": "张三",
"age": 26,
"gender": "男",
"grade": {
"Chinese": 96,
"Math": 99
},
"records": [
{
"Chinese": 95,
"Math": 100
},
{
"Chinese": 98,
"Math": 98
}
]
}
# 获取对象值
search_name = 'name'
res_name = jmespath.search(search_name, data)
print(res_name)
search_records = 'records'
res_records = jmespath.search(search_records, data)
print(res_records)
运行结果:
3、子表达式取值
示例代码:
import jmespath
data = {
"name": "张三",
"age": 26,
"gender": "男",
"grade": {
"Chinese": 96,
"Math": 99
},
"records": [
{
"Chinese": 95,
"Math": 100
},
{
"Chinese": 98,
"Math": 98
}
]
}
# 使用子表达式获取值,若值不存在返回null
search_sub_value = 'grade.Chinese'
res_sub_value = jmespath.search(search_sub_value, data)
print(res_sub_value)
search_sub_value1 = 'records[0].Chinese'
res_sub_value1 = jmespath.search(search_sub_value1, data)
print(res_sub_value1)
search_sub_value2 = 'records[2].Chinese'
res_sub_value2 = jmespath.search(search_sub_value2, data)
print(res_sub_value2)
运行结果:
4、列表取值(对象嵌套列表)
索引表达式,从 0 开始,索引越界,则返回 null;索引可以为负数,-1 为列表中最后一个元素。
示例代码:
import jmespath
data = {
"name": "张三",
"age": 26,
"gender": "男",
"grade": {
"Chinese": 96,
"Math": 99
},
"records": [
{
"Chinese": 95,
"Math": 100
},
{
"Chinese": 98,
"Math": 98
}
]
}
# 列表取值,索引从0开始,若是
search_list1 = 'records[1].Chinese'
res_list1 = jmespath.search(search_list1, data)
print(res_list1)
search_list2 = 'records[2].Chinese'
res_list2 = jmespath.search(search_list2, data)
print(res_list2)
search_list3 = 'records[-1].Chinese'
res_list3 = jmespath.search(search_list3, data)
print(res_list3)
运行结果:
5、切片
与 python 列表切片相同,[start:end:step],留头掐尾
示例代码:
import jmespath
data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ret1 = jmespath.search("[0:5]", data)
print(ret1)
ret2 = jmespath.search("[5:10]", data)
print(ret2)
ret3 = jmespath.search("[::2]", data)
print(ret3)
运行结果:
6、列表投影
通配符 *,表示返回列表中的全部元素。当单组元素的结果表达式为 null,则该值从收集的 结果集 中忽略。
示例代码:
import jmespath
data = {
"student": [
{"name": "james", "age": 32},
{"name": "harden", "age": 18},
{"name": "curry", "age": 13},
{"test": "ok"}
]
}
ret1 = jmespath.search("student[*]", data)
print(ret1)
ret2 = jmespath.search("student[*].name", data)
print(ret2)
# 切片投影
ret3 = jmespath.search("student[:2].name", data)
print(ret3)
运行结果:
7、对象投影
示例代码:
import jmespath
data = {
"test": {
"funcA": {"num": 1},
"funcB": {"num": 2},
"funcC": {"miss": 3},
}
}
ret1 = jmespath.search("test.*", data)
print(ret1)
ret2 = jmespath.search("test.*.num", data)
print(ret2)
运行结果:
8、展平投影
列表/对象投影,在投影中创建投影时会保留原始文档的结构。
如果只要列表下的所有值,不关心所谓的层级结构,那么就需要通过展平投影来获取结果。
只需要将 [*] -> [] 就可以扁平化列表。它将子列表展平到父列表,并不是递归的关系
示例代码:
import jmespath
data = {
"a": [
{
"b": [
{"name": "a"},
{"name": "b"}
]
},
{
"b": [
{"name": "c"},
{"name": "d"}
]
}
]
}
ret1 = jmespath.search("a[*].b[*].name", data)
print(ret1)
ret2 = jmespath.search("a[*].b[].name", data)
print(ret2)
ret3 = jmespath.search("a[].b[].name", data)
print(ret3)
ret4 = jmespath.search("a[].b[*].name", data)
print(ret4)
ret5 = jmespath.search("a[].b[]", data)
print(ret5)
运行结果:
9、过滤投影
过滤器表达式是为数组定义的,一般形式为:
左侧投影 [? <表达式> <比较器> <表达式>] 右侧投影
条件表达式支持如下:
- ==, tests for equality.
- !=, tests for inequality.
- <, less than.
- <=, less than or equal to.
- >, greater than.
- >=, greater than or equal to.
示例代码:
import jmespath
data = {
"book": [
{"name": "a1", "author": "aa"},
{"name": "a2", "author": "aa"},
{"name": "b", "author": "bb"}
]
}
ret1 = jmespath.search("book[?author=='aa'].name", data)
print(ret1)
运行结果:
10、管道表达式
管道表达式 <expression> | <expression>,表示必须停止投影。将当前节点的结果传递到管道符右侧继续投影。
示例代码:
import jmespath
data = {
"book": [
{"name": "a1", "author": "aa"},
{"name": "a2", "author": "aa"},
{"name": "b", "author": "bb"}
]
}
ret1 = jmespath.search("book[*].name", data)
print(ret1)
ret2 = jmespath.search("book[*].name | [1]", data)
print(ret2)
运行结果:
11、多选列表
[name, state.name] 表达式的意思是创建一个包含两个元素的列表:
- 第一个元素是计算 name 表达式得到的结果
- 第二个元素是计算 state.name 表达式的结果
因此,每个列表元素都会创建一个双元素列表,整个表达式的最终结果是一个包含两个元素列表的列表。
示例代码:
import jmespath
data = {
"people": [
{
"name": "a",
"state": {"name": "up"}
},
{
"name": "b",
"state": {"name": "down"}
},
{
"name": "c",
"state": {"name": "up"}
}
]
}
ret = jmespath.search("people[].[name, state.name]", data)
print(ret)
运行结果:
12、多选对象
与多选列表思想相同,创建的是一个散列而不是数组。
示例代码:
import jmespath
data = {
"people": [
{
"name": "a",
"state": {"name": "up"}
},
{
"name": "b",
"state": {"name": "down"}
},
{
"name": "c",
"state": {"name": "up"}
}
]
}
ret = jmespath.search("people[].{name: name, state_name: state.name}", data)
print(ret)
运行结果:
13、函数的使用(包含常用的一些内置函数)
示例代码:
import jmespath
class TestJmesPath(object):
def __init__(self):
self.data = {
"book": [
{"name": "平凡的世界", "author": "路遥", "sort": 3},
{"name": "围城", "author": "钱钟书", "sort": 2},
{"name": "围城", "author": "钱钟书", "sort": 2},
{"name": "活着", "author": "余华", "sort": 1},
{"name": "麦田里的守望者", "author": "塞林格", "sort": 4},
{"name": "挪威的森林", "author": "村上春树", "sort": 5}
]
}
def test_keys(self):
"""提取对象的 key """
result = jmespath.search("book[0].name", self.data)
return result
def test_values(self):
"""提取对象的 value,不接受数组"""
result = jmespath.search("book[0] | values(@)", self.data)
return result
def test_sort(self):
"""根据 sort 进行排序"""
result = jmespath.search("book[*].sort | sort(@)", self.data)
return result
def test_sort2(self):
result = jmespath.search("book[*].author | sort(@) | [join(', ', @)]", self.data)
return result
def test_type(self):
result = jmespath.search("book[*].name | type(@)", self.data)
return result
def test_type2(self):
result = jmespath.search("book[0].name | type(@)", self.data)
return result
def test_to_string(self):
result = jmespath.search('[].to_string(@)', [1, 2, 3, "number", True])
return result
def test_to_number(self):
result = jmespath.search('[].to_number(@)', ["1", "2", "3", "number", True])
return result
def test_contains(self):
result = jmespath.search("contains(`foobar`, `foo`)", {})
return result
def test_contains2(self):
result = jmespath.search("contains(`foobar`, `f123`)", {})
return result
def test_join(self):
# expected one of: ['array-string']
# @ 为当前节点,得到的结果用逗号加空格分隔,然后放在当前节点下
result = jmespath.search("join(`, `, @)", ["a", "b"])
return result
def test_length(self):
result = jmespath.search("length(@)", ["a", "b"])
return result
def test_max(self):
result = jmespath.search("max(@)", [10, 3, 5, 5, 8])
return result
def test_min(self):
result = jmespath.search("min(@)", [10, 3, 5, 5, 8])
return result
if __name__ == '__main__':
obj = TestJmesPath()
print(obj.test_keys())
print(obj.test_values())
print(obj.test_sort())
print(obj.test_sort2())
print(obj.test_type())
print(obj.test_type2())
print(obj.test_to_string())
print(obj.test_to_number())
print(obj.test_contains())
print(obj.test_contains2())
print(obj.test_join())
print(obj.test_length())
print(obj.test_max())
print(obj.test_min())
运行结果:
14、自定义函数
示例代码:
from jmespath import search
from jmespath import functions
import jmespath
class CustomFunctions(functions.Functions):
@functions.signature({'types': ['string', "array"]})
def _func_uniq(self, arg):
if isinstance(arg, str):
# string of unique
return ''.join(sorted(set(arg)))
if isinstance(arg, list):
# array of unique
return sorted(set(arg))
options = jmespath.Options(custom_functions=CustomFunctions())
aa = search("foo.bar", {'foo': {'bar': 'banana'}})
print(aa)
bb = search("foo.bar | uniq(@)", {'foo': {'bar': 'banana'}}, options=options)
print(bb)
cc = search("foo.bar", {'foo': {'bar': [5, 5, 2, 1]}})
print(cc)
dd = search("foo.bar | uniq(@)", {'foo': {'bar': [5, 5, 2, 1]}}, options=options)
print(dd)
运行结果:
15、包含:contains(@, 'foo'),@关联到当前级别的元素值
示例代码:
import jmespath
data = {
"name": ['张三', '李四', '张三丰', '王五']
}
ret = jmespath.search("name[?contains(@, '张')]", data)
print(ret)
运行结果:
16、排序:sort_by(contents, &Date)
示例代码:
import jmespath
data = {
"people": [{"name": "张三", 'age': 25}, {"name": "李四", 'age': 26}, {"name": "王五", 'age': 23}]
}
ret = jmespath.search("sort_by(people, &age)", data)
print(ret)
运行结果:
参考博文:
JMESPath 基本操作_abee-tester的博客-CSDN博客_jmespath