Python-jmespath学习

基础表达式

基础表达语法,类似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))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,729评论 0 8
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,149评论 0 9
  • 为什么用elasticsearch 在引入elasticsearch前,我们的数据一般都存储在mysql上,所有的...
    递归宇宙阅读 977评论 0 0
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 123,999评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,030评论 0 4