JMESPath 是 JSON 查询语言,可以从 JSON 文档中提取和转换元素。在做接口自动化测试项目时,最基础的一步就是从响应中获取各种待验证的字段值,掌握 jmespath 语法,能达到事半功倍的效果。
官方文档:
安装
pip install jmespath
jmespath.py 库提供了两个接口:
函数说明:
示例代码:
- 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)
运行结果:
示例代码:
- 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)
运行结果:
示例代码:
- 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)
运行结果:
索引表达式,从 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)
运行结果:
与 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)
运行结果:
通配符 *,表示返回列表中的全部元素。当单组元素的结果表达式为 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)
运行结果:
示例代码:
- 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)
-
运行结果:
列表/对象投影,在投影中创建投影时会保留原始文档的结构。
如果只要列表下的所有值,不关心所谓的层级结构,那么就需要通过展平投影来获取结果。
只需要将 [*] -> [] 就可以扁平化列表。它将子列表展平到父列表,并不是递归的关系
示例代码:
- 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)
运行结果:
过滤器表达式是为数组定义的,一般形式为:
左侧投影 [? <表达式> <比较器> <表达式>] 右侧投影
条件表达式支持如下:
示例代码:
- 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)
运行结果:
管道表达式
示例代码:
- 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)
运行结果:
[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)
运行结果:
与多选列表思想相同,创建的是一个散列而不是数组。
示例代码:
- 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)
运行结果:
示例代码:
- 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())
-
运行结果:
示例代码:
- 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)
运行结果:
示例代码:
- import jmespath
-
- data = {
- "name": ['张三', '李四', '张三丰', '王五']
- }
-
- ret = jmespath.search("name[?contains(@, '张')]", data)
- print(ret)
运行结果:
示例代码:
- import jmespath
-
- data = {
- "people": [{"name": "张三", 'age': 25}, {"name": "李四", 'age': 26}, {"name": "王五", 'age': 23}]
- }
-
- ret = jmespath.search("sort_by(people, &age)", data)
- print(ret)
运行结果:
参考博文: