Python中jmespath解析提取json数据

王大力测试进阶之路

共 10237字,需浏览 21分钟

 ·

2021-11-14 10:50

      

        在做接口自动化,测试断言时,我们经常需要提取接口的的响应数据字段,以前用过jsonpath,有几篇相关文章,可以参考下(Python深层解析json数据之JsonPath【Jmeter篇】后置处理器之正则提取器、Json提取器 、Jmeter之json提取器实战(二)Jmeter之json条件提取实战(三) )今天我们来介绍下jmespath用法,可以帮我们进行数据的灵活提取,下面通过案例来说明jmespath在python的使用。

jmespath官方文档

https://jmespath.org/tutorial.html#projections

jmespath安装

pip install jmespath

字典,通过key名称提取

import jmespathdict_1 = {"a": "foo", "b": "bar", "c": "baz"}print(jmespath.search("c",dict_1))
baz

嵌套字典,层级提取

import jmespathdict_1 = {"a": {"b": {"c": {"d": "value"}}}}print(jmespath.search("a.b.c.d",dict_1))
value
import jmespathdict_1 = {"a": {"b": {"c": {"d": "value"}}}}print(jmespath.search("a.b.c",dict_1))
{'d': 'value'}

列表,通过索引提取

import jmespathlist_1 = ["a", "b", "c", "d", "e", "f"]print(jmespath.search("[1]",list_1))
b

列表、字典嵌套提取

import jmespathsource = {"a": {  "b": {    "c": [      {"d": [0, [1, 2]]},      {"d": [3, 4]}    ]  }}}print(jmespath.search("a.b.c[0].d[1][0]",source))
1

切片提取列表中值

import jmespathsource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(jmespath.search("[0:5]",source))
[0, 1, 2, 3, 4]
import jmespathsource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(jmespath.search("[5:10]",source))
[5, 6, 7, 8, 9]
import jmespathsource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(jmespath.search("[:5]",source))
[0, 1, 2, 3, 4]
import jmespathsource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(jmespath.search("[::2]",source))
[0, 2, 4, 6, 8]

列表取值使用 * 通配符

1、取出列表中所有 first对应的值

 people[*].first

import jmespathsource = {  "people": [    {"first": "James", "last": "d"},    {"first": "Jacob", "last": "e"},    {"first": "Jayden", "last": "f"},    {"missing": "different"}  ],  "foo": {"bar": "baz"}}print(jmespath.search("people[*].first",source))
['James', 'Jacob', 'Jayden']

2、取出列表中前2个first 对应的值

people[:2].first

import jmespathsource = {  "people": [    {"first": "James", "last": "d"},    {"first": "Jacob", "last": "e"},    {"first": "Jayden", "last": "f"},    {"missing": "different"}  ],  "foo": {"bar": "baz"}}print(jmespath.search("people[:2].first",source))
['James', 'Jacob']

对象取值使用 * 通配符

取出ops 对象的任意属性对应的numArgs值 ops.*.numArgs

import jmespathsource = {  "ops": {    "functionA": {"numArgs": 2},    "functionB": {"numArgs": 3},    "functionC": {"variadic": 1}  }}print(jmespath.search("ops.*.numArgs",source))
[2, 3]

子查询使用 * 通配符

在查询的结果中继续使用 * 通配符,查询的结果是列表的列表

import jmespathsource = {  "reservations": [    {      "instances": [        {"state": "running"},        {"state": "stopped"}      ]    },    {      "instances": [        {"state": "terminated"},        {"state": "running"}      ]    }  ]}print(jmespath.search("reservations[*].instances[*].state",source))
[['running', 'stopped'], ['terminated', 'running']]

我们希望结果为[“ running”, “ stopped”, “ terminated”, “ running”]一个状态列表,可以使用 [] 而不是 [*]

print(jmespath.search("reservations[].instances[].state",source))
['running', 'stopped', 'terminated', 'running']

过滤器使用

过滤器表达式是为数组定义的,其一般形式为 [?<表达式> <比较器> <表达式>]。

常用的比较表达式可以使用 ==, !=, <, <=, >, > =

假设我们有一个设备列表,每个设备都有一个名称和一个 state。我们想要所有正在运行的计算机的名称

import jmespathsource = {  "machines": [    {"name": "a", "state": "running"},    {"name": "b", "state": "stopped"},    {"name": "b", "state": "running"}  ]}print(jmespath.search("machines[?state=='running'].name",source))
['a', 'b']

管道表达式

前面在匹配list里面的多个值时候,查询的结果是一个list,如果我想取出结果里面的第一个可以使用管道符 |

取出people下所有对象的 first 属性,从结果里面取第一个值:people[*].first | [0]

import jmespathsource = {  "people": [    {"first": "James", "last": "d"},    {"first": "Jacob", "last": "e"},    {"first": "Jayden", "last": "f"},    {"missing": "different"}  ],  "foo": {"bar": "baz"}}print(jmespath.search("people[*].first | [0]",source))
James

多选列表创建一个列表

到目前为止,我们已经研究了JMESPath表达式,这些表达式有助于将JSON文档缩减为您感兴趣的元素。下一个概念, 多选列表和 多选哈希允许您创建JSON元素。这使您可以创建JSON文档中不存在的元素。多选列表创建一个列表,多选哈希创建一个JSON对象。

这是一个多选列表的示例:people[].[name, state.name]

import jmespathsource = {  "people": [    {      "name": "a",      "state": {"name": "up"}    },    {      "name": "b",      "state": {"name": "down"}    },    {      "name": "c",      "state": {"name": "up"}    }  ]}print(jmespath.search("people[].[name, state.name]",source))
[['a', 'up'], ['b', 'down'], ['c', 'up']]


在上面的表达式中,[name, state.name]部分是一个多选列表。它说要创建一个由两个元素组成的列表,第一个元素是针对list元素评估名称表达式的结果,第二个元素是对state.name评估的结果。因此,每个列表元素将创建一个两个元素列表,并且整个表达式的最终结果是两个元素列表的列表。

与投影不同,即使结果为null,也始终包含表达式的结果。如果将以上表达式更改为people []。[foo, bar],则每个两个元素列表将为[null, null]。

多重选择具有与多重选择列表相同的基本概念,不同之处在于它会创建哈希而不是数组。使用上面的相同示例,如果我们想创建一个具有两个键Name和 State的两个元素哈希,则可以使用以下代码:

import jmespathsource = {  "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}",source))
[{'Name': 'a', 'State': 'up'}, {'Name': 'b', 'State': 'down'}, {'Name': 'c', 'State': 'up'}]

函数的使用

JMESPath支持函数表达式,例如:length(people)

import jmespathsource = {  "people": [    {      "name": "b",      "age": 30,      "state": {"name": "up"}    },    {      "name": "a",      "age": 50,      "state": {"name": "down"}    },    {      "name": "c",      "age": 40,      "state": {"name": "up"}    }  ]}print(jmespath.search("length(people)",source))
3

函数可用于以强大的方式转换和过滤数据。可以在此处找到函数的完整列表,并且 函数表达式规范具有完整的详细信息。

以下是一些功能示例。

本示例在people数组中打印最老的人的名字:

import jmespathsource = {  "people": [    {      "name": "b",      "age": 30    },    {      "name": "a",      "age": 50    },    {      "name": "c",      "age": 40    }  ]}print(jmespath.search("max_by(people, &age).name",source))
a

函数也可以与过滤器表达式组合。在下面的示例中,JMESPath表达式在myarray中查找包含字符串foo的所有元素。

import jmespathsource = {  "myarray": [    "foo",    "foobar",    "barfoo",    "bar",    "baz",    "barbaz",    "barfoobaz"  ]}print(jmespath.search("myarray[?contains(@, 'foo') == `true`]",source))
['foo', 'foobar', 'barfoo', 'barfoobaz']


场景一,接口响应数据,提取code、msg、status字段进行断言

source = {  "code": 0,  "msg": "成功",  "trace": "ad12de4",  "data": {    "total": 205,    "list": [{      "id": 15000087,      "name": "促销员",      "job_nature": 2,      "category_id": 61,      "user_id": 589601,      "company_id": 5084,      "group_id": 5084,      "status": 4,      "audit_type": 0,      "company_name": "二十二门店",      "job_nature_zh": "兼职",      "salary_zh": "1000元\/时",      "show_status": 5,      "manage_status_reason": "",      "status_zh": "停招",      "browse_users_num": 0,      "communication_users_num": 0,      "sign_up_users_num": 0,      "job_card_time_remaining": 22,      "job_top_card_time_remaining": 0    }, {      "id": 15000078,      "name": "促销员",      "job_nature": 1,      "category_id": 61,      "user_id": 589601,      "company_id": 1000064,      "group_id": 5084,      "status": 4,      "audit_type": 100,      "company_name": "二十二门店",      "job_nature_zh": "全职",      "salary_zh": "2000-3000元\/月",      "show_status": 6,      "manage_status_reason": "",      "status_zh": "停招",      "browse_users_num": 0,      "communication_users_num": 0,      "sign_up_users_num": 0,      "job_card_time_remaining": 0,      "job_top_card_time_remaining": 0    }, {      "id": 15000077,      "name": "促销员",      "job_nature": 1,      "category_id": 61,      "user_id": 589601,      "company_id": 5084,      "group_id": 5084,      "status": 4,      "audit_type": 100,      "company_name": "二十二门店",      "job_nature_zh": "全职",      "salary_zh": "2000-3000元\/月",      "show_status": 2,      "manage_status_reason": "",      "status_zh": "停招",      "browse_users_num": 0,      "communication_users_num": 0,      "sign_up_users_num": 0,      "job_card_time_remaining": 0,      "job_top_card_time_remaining": 0    }, {      "id": 13076362,      "name": "收银员",      "job_nature": 2,      "category_id": 97,      "user_id": 589601,      "company_id": 5084,      "group_id": 5084,      "status": 4,      "audit_type": 1,      "company_name": "二十二门店",      "job_nature_zh": "兼职",      "salary_zh": "1000元\/时",      "show_status": 2,      "manage_status_reason": "",      "status_zh": "停招",      "browse_users_num": 0,      "communication_users_num": 0,      "sign_up_users_num": 0,      "job_card_time_remaining": 0,      "job_top_card_time_remaining": 0    }, {      "id": 13076361,      "name": "品类管理",      "job_nature": 1,      "category_id": 102,      "user_id": 589601,      "company_id": 5084,      "group_id": 5084,      "status": 1,      "audit_type": 1,      "company_name": "二十二门店",      "job_nature_zh": "全职",      "salary_zh": "2000-3000元\/月",      "show_status": 1,      "manage_status_reason": "",      "status_zh": "招聘中",      "browse_users_num": 3,      "communication_users_num": 1,      "sign_up_users_num": 1,      "job_card_time_remaining": 342,      "job_top_card_time_remaining": 0    }]  }}
print(jmespath.search("code",source)) # 0print(jmespath.search("msg",source)) # 成功print(jmespath.search("data.list[].status",source)) # [4, 4, 4, 4, 1]

场景二,接口响应数据,提取列表类模块某字段进行断言

import jmespathsource = [{  "Name": "晨练指数",  "ID": 100,  "Ascending": 1,  "LocalDateTime": "2021-11-11T07:00:00+08:00",  "EpochDateTime": 1636585200,  "Value": 4.0,  "Category": "不宜",  "CategoryValue": 4,  "MobileLink": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn",  "Link": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn"}, {  "Name": "穿衣指数",  "ID": 101,  "Ascending": 1,  "LocalDateTime": "2021-11-11T07:00:00+08:00",  "EpochDateTime": 1636585200,  "Value": 5.0,  "Category": "初冬装",  "CategoryValue": 5,  "MobileLink": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn",  "Link": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn"}, {  "Name": "感冒指数",  "ID": 102,  "Ascending": 1,  "LocalDateTime": "2021-11-11T07:00:00+08:00",  "EpochDateTime": 1636585200,  "Value": 2.0,  "Category": "较易发",  "CategoryValue": 2,  "MobileLink": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn",  "Link": "http://m.weathercn.com/zh/cn/pudong-new-district/74761/weather-forecast/74761?lang=zh-cn"}]
print(jmespath.search("[].Name",source)) 
['晨练指数', '穿衣指数', '感冒指数']


浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报