基础表达式
基础表达语法,类似JavaScript的取列表值、对象值,互相组合取嵌套值。
import jmespath
print(jmespath.search('a', {"a": "foo", "b": "bar", "c": "baz"}))
print(jmespath.search('a.b.c.d', {"a": {"b": {"c": {"d": "value"}}}}))
print(jmespath.search('[1]', ["a", "b", "c", "d", "e", "f"]))
print(jmespath.search('a.b.c[0].d[1][0]', {"a": {"b": {"c": [{"d": [0, [1, 2]]}, {"d": [3, 4]}]}}}))
# 输出
foo
value
b
1
切片
和Python的列表切片语法完全一致。
import jmespath
print(jmespath.search('[0:5]', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
print(jmespath.search('[5:10]', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
print(jmespath.search('[:5]', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
print(jmespath.search('[::2]', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
print(jmespath.search('[::-1]', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
# output
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
[0, 1, 2, 3, 4]
[0, 2, 4, 6, 8]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
列表和切片投影
[*]
第一个只包含三个元素,尽管people数组有四个元素。这是因为当表达式第一次应用时,最后一个元素{"missing": "different"}的值为空,空值不会被添加到收集的结果数组中。如果您尝试表达式foo[*].bar
,你会看到一个null的结果,因为与foo
键相关联的值是一个JSON对象,而不是一个数组,并且列表投影仅为JSON数组定义。
切片投影和列表投影几乎是一样的,唯一不同的是,左边的投影是计算切片的结果,它可能不包括原始列表中的所有元素。
import jmespath
data = {
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
print(jmespath.search('people[*].first', data)) # 列表投影
print(jmespath.search('people[0:3].first', data)) # 切片投影
# output
['James', 'Jacob', 'Jayden']
['James', 'Jacob', 'Jayden']
对象投影
列表投影是为JSON数组定义的,而对象投影是为JSON对象定义的。可以使用*
语法创建对象投影。这将创建JSON对象的值列表,并将投影的右侧投影到值列表上。
import jmespath
data = {
"ops": {
"functionA": {"numArgs": 2},
"functionB": {"numArgs": 3},
"functionC": {"variadic": True},
"functionD": 3
}
}
print(jmespath.search('ops.*', data)) # 创建JSON对象的值列表
print(jmespath.search('ops.*.numArgs', data))
# output
[{'numArgs': 2}, {'numArgs': 3}, {'variadic': True}, 3]
[2, 3]
Flatten Projections(扁平投影)
下面是列表投影和对象投影组合使用,结果嵌套了很多层列表,可读性差。
我们希望得到一个结果["running", "stopped", "terminated", "running"]
。在这种情况下,我们不关心实例属于哪个保留,我们只需要一个列表。
这就是Flatten投影解决的问题。要得到想要的结果,可以使用[]
而不是[*]
来平铺列表:尝试将上面表达式中的[*]
更改为[]
import jmespath
data = {
"reservations": [
{
"instances": [
{"state": "running"},
{"state": "stopped"}
]
},
{
"instances": [
{"state": "terminated"},
{"state": "running"}
]
}
]
}
print(jmespath.search('reservations[*].instances[*]', data))
print(jmespath.search('reservations[*].instances[*].*', data))
print(jmespath.search('reservations[*].instances[*].state', data))
# output
[[{'state': 'running'}, {'state': 'stopped'}], [{'state': 'terminated'}, {'state': 'running'}]]
[[['running'], ['stopped']], [['terminated'], ['running']]]
[['running', 'stopped'], ['terminated', 'running']]
结果就很符合期望了,看起来很顺眼。
import jmespath
data = {
"reservations": [
{
"instances": [
{"state": "running"},
{"state": "stopped"}
]
},
{
"instances": [
{"state": "terminated"},
{"state": "running"}
]
}
]
}
print(jmespath.search('reservations[].instances[].state', data))
# output
['running', 'stopped', 'terminated', 'running']
当然,也可以直接使用[]
or[][]
进行列表扁平投影。
import jmespath
data = [
[0, 1],
2,
[3],
4,
[5, [6, 7]]
]
print(jmespath.search('[*]', data))
print(jmespath.search('[]', data))
print(jmespath.search('[][]', data))
# output
[[0, 1], 2, [3], 4, [5, [6, 7]]]
[0, 1, 2, 3, 4, 5, [6, 7]]
[0, 1, 2, 3, 4, 5, 6, 7]
过滤投影
如同其名称,过滤投影的作用就是去除不满足过滤条件的数据,语法:LHS [? <expression> <comparator> <expression>] RHS
,comparators支持这些 ==, !=, <, <=, >, >=
。
下面的例子就是获取状态为running
的机器名称。
import jmespath
data = {
"machines": [
{"name": "a", "state": "running"},
{"name": "b", "state": "stopped"},
{"name": "c", "state": "running"}
]
}
print(jmespath.search("machines[?state=='running'].name", data))
# output
['a', 'c']
上面那个例子的过滤条件是字符串status=="running"
,但有时候会遇到过滤条件是number整数类型。
下面这个例子想获取exchange_type==13
的数据,刚开始是这么写的[?exchange_type==13]
,也试了[?exchange_type=='13']
,结果是报错或者是数据为空。正确的写法是"data.exchange_config_list[?exchange_type==`13`]"
,注意不是单引号也不是双引号,是反单引号。
import jmespath
data = {"code": 200, "data": {"base_rsp": {"ret": 0, "rsp_msg": "", "seq": 0}, "exchange_config_list": [
{"already_exchange_num": 1, "exchange_id": 30217, "exchange_type": 13, "expired_day": 0, "fragments_num": 1,
"fragments_type": 1, "most_exchange_num": 1, "weight": 10000},
{"already_exchange_num": 485, "exchange_id": 90124, "exchange_type": 1, "expired_day": 10, "fragments_num": 3,
"fragments_type": 2, "most_exchange_num": 99999, "weight": 999},
{"already_exchange_num": 0, "exchange_id": 30174, "exchange_type": 13, "expired_day": 0, "fragments_num": 6570,
"fragments_type": 1, "most_exchange_num": 2, "weight": 1}]}, "elapsed_time": 7, "message": "请求成功", "ret": 0}
print(jmespath.search("data.exchange_config_list[?exchange_type==`13`].exchange_id", data))
# output
[30217, 30174]
多条件过滤投影,有||,&&,>,<
等等。
import jmespath
data = {"code": 200, "data": {"base_rsp": {"ret": 0, "rsp_msg": "", "seq": 0}, "exchange_config_list": [
{"already_exchange_num": 1, "exchange_id": 30217, "exchange_type": 13, "expired_day": 0, "fragments_num": 1,
"fragments_type": 1, "most_exchange_num": 1, "weight": 10000},
{"already_exchange_num": 485, "exchange_id": 90124, "exchange_type": 1, "expired_day": 10, "fragments_num": 3,
"fragments_type": 2, "most_exchange_num": 99999, "weight": 999},
{"already_exchange_num": 0, "exchange_id": 30174, "exchange_type": 13, "expired_day": 0, "fragments_num": 6570,
"fragments_type": 1, "most_exchange_num": 2, "weight": 1}]}, "elapsed_time": 7, "message": "请求成功", "ret": 0}
print(jmespath.search("data.exchange_config_list[?exchange_type==`13`].exchange_id", data))
print(jmespath.search("data.exchange_config_list[?exchange_type==`13` || exchange_type==`1`].exchange_id", data))
print(jmespath.search("data.exchange_config_list[?exchange_type==`13` && weight==`1`].exchange_id", data))
# output
[30217, 30174]
[30217, 90124, 30174]
[30174]
管道符|
语法
投影是JMESPath中的一个重要概念。然而,有些时候投影语义并不是您想要的。一种常见的情况是,您希望操作投影的结果,而不是将表达式投影到数组中的每个元素上。例如,表达式people[*]
。First将为您提供一个包含people数组中每个人的名字的数组。如果你想要列表中的第一个元素呢?如果你试过别人[*]。第一个[0]
,您只是为people数组中的每个元素计算第一个[0]
,并且由于没有为字符串定义索引,最终结果将是一个空数组。要实现预期的结果,可以使用管道表达式<表达式> | <表达式>
来指示必须停止投影。
如下面的例子所示,后面新增一个表达式对数据进行处理,可以是过滤条件,有可能是索引表达式,得到期望的数据。
import jmespath
data = {
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
print(jmespath.search("people[*]", data))
print(jmespath.search("people[*] | [?last=='e']", data))
print(jmespath.search("people[*].first", data))
print(jmespath.search("people[*].first | [0]", data))
# output
[{'first': 'James', 'last': 'd'}, {'first': 'Jacob', 'last': 'e'}, {'first': 'Jayden', 'last': 'f'}, {'missing': 'different'}]
[{'first': 'Jacob', 'last': 'e'}]
['James', 'Jacob', 'Jayden']
James
MultiSelect多选
多选列表和多选散列允许您创建JSON元素。这允许您创建JSON对象中不存在的元素。多选列表创建一个列表,多选散列创建一个JSON对象。
说人话,多选散列就是多选哈希,也就是多选字典。
列表多选
如下面的例子,列表多选创建JSON对象中不存在的元素。
import jmespath
data = {
"people": [
{
"name": "a",
"state": {"name": "up"}
},
{
"name": "b",
"state": {"name": "down"}
},
{
"name": "c",
"state": {"name": "up"}
}
]
}
print(jmespath.search("people[].name", data))
print(jmespath.search("people[].[name, state.name]", data))
# output
['a', 'b', 'c']
[['a', 'up'], ['b', 'down'], ['c', 'up']]
对象多选,又称哈希多选,又称散列多选。
如下面的例子,key
值是自己定义,value
值是表达式计算出来的。
import jmespath
data = {
"people": [
{
"name": "a",
"state": {"name": "up"}
},
{
"name": "b",
"state": {"name": "down"}
},
{
"name": "c",
"state": {"name": "up"}
}
]
}
print(jmespath.search("people[].{Name: name, State: state.name}", data))
# output
[{'Name': 'a', 'State': 'up'}, {'Name': 'b', 'State': 'down'}, {'Name': 'c', 'State': 'up'}]
Functions函数
jmespath有内置函数处理数据,文档如下内置函数
length(),获取列表长度,适用于列表对象。
import jmespath
data = {
"people": [
{
"name": "b",
"age": 30
},
{
"name": "a",
"age": 50
},
{
"name": "c",
"age": 40
}
]
}
print(jmespath.search("length(people)", data))
# output
3
max_by(),以某个值为维度,取最大值。
如下面例子,取people
列表age
最大的列表元素
import jmespath
data = {
"people": [
{
"name": "b",
"age": 30
},
{
"name": "a",
"age": 50
},
{
"name": "c",
"age": 40
}
]
}
print(jmespath.search('max_by(people, &age)', data))
# output
{'name': 'a', 'age': 50}
contains(),是否包含某个元素
下面的例子是获取myarray列表中包含foo
的元素。
import jmespath
data = {
"myarray": [
"foo",
"foobar",
"barfoo",
"bar",
"baz",
"barbaz",
"barfoobaz"
]
}
print(jmespath.search("myarray[?contains(@, 'foo') == `true`]", data))
# output
['foo', 'foobar', 'barfoo', 'barfoobaz']
@指向数据问题
如下面两个例子得出,@指向当前操作的列表对象,也有可能是字典对象。
import jmespath
data1 = {
"myarray": [
"foo",
"foobar",
"barfoo",
"bar",
"baz",
"barbaz",
"barfoobaz"
]
}
data2 = [
"foo",
"foobar",
"barfoo",
"bar",
"baz",
"barbaz",
"barfoobaz"
]
print(jmespath.search("myarray[?contains(@, 'foo') == `true`]", data1)) # @指向myarray列表
print(jmespath.search("[?contains(@, 'foo') == `true`]", data2)) # @指向data2列表
# output
['foo', 'foobar', 'barfoo', 'barfoobaz']
['foo', 'foobar', 'barfoo', 'barfoobaz']
import jmespath
data1 = {
"people": [
{
"name": "b",
"age": 30
},
{
"name": "a",
"age": 50
},
{
"name": "c",
"age": 40
}
]
}
data2 = [
{
"name": "b",
"age": 30
},
{
"name": "a",
"age": 50
},
{
"name": "c",
"age": 40
}
]
print(jmespath.search('max_by(people, &age)', data1))
print(jmespath.search('max_by(@, &age)', data2))
# output
{'name': 'a', 'age': 50}
{'name': 'a', 'age': 50}
数字作为key值,keys和values方法取字典的键值
key值是number数据类型,取值如下:
import jmespath
my_list = {
"code": 200,
"data": {
"base_rsp": {
"ret": 0,
"rsp_msg": "",
"seq": 0
},
"uid_follow_status_map": {
"17230530": 1
}
},
"elapsed_time": 9,
"message": "\u8bf7\u6c42\u6210\u529f",
"ret": 0
}
print(jmespath.search('data.uid_follow_status_map."17230530"', my_list)) #数字作为键值,需要用双引号,且单引号在外边
print(jmespath.search('values(data.uid_follow_status_map)|[0]', my_list)) #取字典的key或者value值,使用values方法和keys方法
##输出
1
1
httprunner框架extract提取语法问题
httprunner项目使用jmespath提取字段,但是不完全支持,存在以下问题
表达式不支持传递参数
表达式不能格式化传参:body.data.bbs_id_list[?exchange_type={}].formmat(type)
列表切片索引不支持负数
如body.data.bbs_id_list[-1]
,这个在httprunner执行提取不了数据,在Python执行环境时正常的。
可以用reverse
方法进行列表反转
print(jmespath.search('data.bbs_id_list[]', data))
print(jmespath.search('reverse(data.bbs_id_list[])', data))
print(jmespath.search('reverse(data.bbs_id_list[])|[0]', data))