jmespath学习4-投影Projections


投影是 JMESPath 的主要功能之一。它允许您将表达式应用于元素集合。有五种投影:

  1. 列表投影
  2. 切片投影
  3. 物体投影
  4. 展平投影
  5. 过滤器投影

1.列表投影

通配符表达式 创建一个列表投影,这是在一个JSON阵列的投影。
列表中嵌套多个字典,每个元素都是json对象每个元素都是key-value都是一样的,如果想拿到某一个key下的所有value

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

可以看到people[]中的通配符 匹配了列表中的所有元素,并且以列表的形式返回。那么这时候取key是first的值,只需要people[*].first

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

可以看到,查找到的元素同样被放到了通配符创建的列表里,并返回。

使用投影时需要记住一些事项。这些在规范的通配符表达式部分有更详细的讨论,但要点是:

  • 投影被评估为两个步骤。左侧 (LHS) 创建初始值的 JSON 数组。投影的右侧 (RHS) 是为左侧创建的 JSON
    数组中的每个元素进行投影的表达式。在评估左侧和/或右侧时,每种投影类型的语义略有不同。
  • 如果投影到单个数组元素上的表达式的结果为 null,则该值将从收集的结果集中省略。
  • 您可以使用管道表达式停止投影。
  • 列表投影仅对 JSON 数组有效。如果该值不是列表,则表达式的结果为null。

2.切片投影

切片投影几乎与列表投影相同,不同之处在于左侧是切片的计算结果,它可能不包括原始列表中的所有元素:

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

在字典的值里再嵌套列表,依然可以被投影到通配符创建的列表里。

import jmespath
 
 
 
dict2 = {
    "people": [
        {"first": "James", "last": "d"},
        {"first": "Jacob", "last": "e"},
        {"first": "Jayden", "last": "f"},
        {"first": [["a","b","c"],2,3,4,5], "last": "g"},
        {"missing": "different"}
    ],
    "foo": {"bar": "baz"}
}
 
res5 = jmespath.search('people[*].first[0]',dict2)
print(res5) # [['a', 'b', 'c']]

无效情况,返回空列表[]

import jmespath
 
 
dict2 = {
    "people": [
        {"first": "James", "last": "d"},
        {"first": "Jacob", "last": "e"},
        {"first": "Jayden", "last": "f"},
        {"first": [["a","b","c"],2,3,4,5], "last": "g"},
        {"missing": "different"}
    ],
    "foo": {"bar": "baz"}
}
 
 
# 下标越界,通配符返回空列表
res6 = jmespath.search('people[*].first[10]',dict2)
print(res6) # []

列表投影仅对list列表有效。如果值不是列表,则表达式的结果为null。
比如,用列表投影取foo[*]就不行了,因为 “foo”: {“bar”: “baz”} 对应的是一个json对象,所以会得到一个null。

import jmespath
 
dict2 = {
    "people": [
        {"first": "James", "last": "d"},
        {"first": "Jacob", "last": "e"},
        {"first": "Jayden", "last": "f"},
        {"first": [["a","b","c"],2,3,4,5], "last": "g"},
        {"missing": "different"}
    ],
    "foo": {"bar": "baz"}
}
 
 
res7 = jmespath.search('foo[*]',dict2)
 
print(res7) # None

3.对象投影

列表投影是为 JSON 数组定义的,而对象投影是为 JSON 对象定义的。您可以使用* 语法创建对象投影。这将创建 JSON 对象的值列表,并将投影的右侧投影到值列表上。

import jmespath
 
dict1 = {
    "ops": {
        "functionA": {"numArgs": 2},
        "functionB": {"numArgs": 3},
        "functionC": {"variadic": True}
    }
}
 
res1 = jmespath.search('ops.*',dict1)
print(res1) # [{'numArgs': 2}, {'numArgs': 3}, {'variadic': True}]
 
res2 = jmespath.search('ops.*.numArgs',dict1)
print(res2) # [2, 3]

可以看到,创建了json列表,并且通配符处于哪个层级的key,就会遍历这个层级key的所有value。
上面的例子中, ops.就是会找 functionA、functionB、functionC,而更进一步ops..numArgs 则是继续往里面找到key为numArgs的值,并放到投影的列表里去返回,所以看的返回的结果是[2, 3] 。  
官方的解释
ops.
.numArgs中的通配符*我们可以看做一个分界线,分为左边和右边,即左边ops,右边numArgs 。

第一步,左边初始化了一个可以投影的数组:

evaluate(ops, inputData) -> [{"numArgs": 2}, {"numArgs": 3},
                             {"variadic": True}]

第二步,右边遍历数组里的每一个元素:

evaluate(numArgs, {numArgs: 2}) -> 2
evaluate(numArgs, {numArgs: 3}) -> 3
evaluate(numArgs, {variadic: true}) -> null

但是因为variadic这个key与 numArgs不匹配,所以返回的是null。
而对于null,是不会添加到最终返回的结果数组里的,所以最终结果只有[2, 3]

4.展平投影

在 JMESPath 表达式中可以使用多个投影。在列表/对象投影的情况下,在投影中创建投影时保留原始文档的结构。

例如,让我们采用表达式reserved[].instances[].state。这个表达式是说顶级键保留有一个数组作为值。对于这些数组元素中的每一个,投影instance[*].state表达式。在每个列表元素中,都有一个实例键,它本身就是一个值,我们为列表中的每个列表元素创建一个子投影。这是一个例子:

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

可以看到,reservations列表中嵌套了字典,而instances的value,又是一个列表。
这时候,用reservations[* ].instances[*].state

我们可以再来看reservations[* ].instances[* ],其实最外层的[] 就是 reservations[* ]创建的.
而内部的每一个实例instances[*],也会各自再创建出投影列表,所以结果中最外层的[]里包含了2个子元素[]
那如果我就是要[‘running’, ‘stopped’, ‘terminated’, ‘running’]

根据上面说的,如果只要保留最外侧的[],
那么内层的实例就不需要再初始化创建[]了,那么就去掉*试一下。

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

总结一下它的2个特点:

  • 它将子列表展平到父列表中(不是递归的,只是一个级别)。
  • 它将创建一个投影,右边的任何内容都将投影到新创建的列表上。 您也可以单独使用[]来展平列表

比如下面例子里有个嵌套列表,先来用[*]看一下,原始的列表结构:

import jmespath
 
 
 
li1 = [
  [0, 1],
  2,
  [3],
  4,
  [5, [6, 7]]
]
 
req1 = jmespath.search('[*]',li1)
print(req1) # [[0, 1], 2, [3], 4, [5, [6, 7]]]

用[]展开列表试一下:

import jmespath
 
li1 = [
  [0, 1],
  2,
  [3],
  4,
  [5, [6, 7]]
]
 
 
req2 = jmespath.search('[]',li1)
print(req2) #[0, 1, 2, 3, 4, 5, [6, 7]]

可以看到,列表成功展开,[0, 1, 2, 3, 4, 5, [6, 7]] ,不是递归展开,只是同级,子列表[6, 7] 与列表其他元素同级。

如果再次展开表达式的结果[][],则会得到[0, 1, 2, 3, 4, 5, 6, 7] 的结果

import jmespath
 
 
li1 = [
  [0, 1],
  2,
  [3],
  4,
  [5, [6, 7]]
]
 
 
req3 = jmespath.search('[][]',li1)
print(req3) # [0, 1, 2, 3, 4, 5, 6, 7]

5.过滤投影

到目前为止,我们已经看过:

  • 列表/切片投影
  • 物体投影
  • 展平投影

评估投影的 RHS 是一种基本类型的过滤器。如果针对单个元素计算的表达式结果为null,则该元素将从最终结果中排除。
过滤投影允许您在 评估投影的 RHS之前过滤投影的 LHS 。

例如,假设我们有一个机器列表,每个机器都有一个name和一个 state。我们想要所有正在运行的机器的名称。

import jmespath
 
dict1 = {
    "machines": [
        {"name": "a", "state": "running"},
        {"name": "b", "state": "stopped"},
        {"name": "b", "state": "running"}
    ]
}
# 过滤  取出state为running的name, 过滤表达式为单引号
res1 = jmespath.search("machines[?state=='running'].name",dict1)

如果您只想要具有指定状态的每台机器的整个 JSON 对象,您还可以删除表达式末尾的.name。

import jmespath
 
dict1 = {
    "machines": [
        {"name": "a", "state": "running"},
        {"name": "b", "state": "stopped"},
        {"name": "b", "state": "running"}
    ]
}
 
 
# 取出state为running的每台机器的json对象
res2 = jmespath.search("machines[?state=='running']",dict1)
print(res2)

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

6.比较运算符

支持以下操作:

  1. ==,测试是否相等
  2. !=,不等式检验
  3. <,小于
  4. <=,小于或等于
  5. >,大于
  6. >=,大于或等于

每个操作的行为取决于每个计算表达式的类型。
每个运算符的比较语义根据对应的 JSON 类型定义如下:

6.1相等运算符

对于字符串/数字/真/假/空类型,相等是完全匹配。一个 字符串等于另一个字符串,如果他们有他们的代码点的精确序列。文字值true/false/null仅等于它们自己的文字值。两个JSON对象相等,如果它们具有相同的一组键和值的(给出了两个JSON objeccts X和ÿ,对于每个键值对(I, j)的在X,存在等效的一对(I, j)的在Ý) . 如果两个 JSON 数组具有相同顺序的相等元素(给定两个数组x和,对于从0到length(x) 的每个i, x[i] == y[i])。

6.2排序运算符

排序运算符>, > =, <, <=是仅适用于数字。使用比较运算符评估任何其他类型将产生空 值,这将导致元素从结果列表中排除。例如,给定:

search('foo[?a<b]', {"foo": [{"a": "char", "b": "char"},
                             {"a": 2, "b": 1},
                             {"a": 1, "b": 2}]})

foo 列表中的三个元素根据a < b进行评估。第一个元素解析为比较"char" < “bar”,并且由于这些类型是字符串,因此表达式结果为null,因此第一个元素不包含在结果列表中。第二个元素解析为2 < 1,这是false,因此第二个元素从结果列表中排除。第三个表达式解析为1 < 2,其计算结果为true,因此第三个元素包含在列表中。该表达式的最终结果是[{“a”: 1, “b”: 2}]

例如

search(foo[?bar==`10`], {"foo": [{"bar": 1}, {"bar": 10}]}) -> [{"bar": 10}]
search( [?bar==`10`], [{"bar": 1}, {"bar": 10}]}) -> [{"bar": 10}]
search(foo[?a==b] , {"foo": [{"a": 1, "b": 2}, {"a": 2, "b": 2}]}) -> [{"a": 2, "b"2}]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值