投影
投影是JMESPath的關鍵特性之一。它允許您將表示式應用於元素集合。有五種投影:
- 列表投影
- 切片投影
- 物件投影
- 展平投影
- 過濾投影
處理投影需要注意的點
- 投影評估分為兩個步驟。左側(LHS)建立一個初始值的JSON陣列。投影的右側(RHS)是要為左側建立的JSON陣列中的每個元素投影的表示式。在計算左側和/或右側時,每個投影型別的語義略有不同。
- 如果投射到單個陣列元素上的表示式的結果為null,則從收集的結果集中忽略該值。
- 可以使用管道表示式停止投影(稍後討論)。
- 列表投影僅對JSON陣列有效。如果值不是列表,則表示式的結果為null。
寫法說明
- []:將子列表展平到父列表中
.
:取字典- *:遍歷每個元素
列表投影
在一個列表中巢狀了字典,而且每一個元素都是一個json物件,它有2個key鍵,分別是first、last,如果你想拿到first下的所有value怎麼辦呢?
import jmespath
dic_1 = {
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
path = jmespath.search("people[*].first", dic_1)
print(path)
# 執行結果
['James', 'Jacob', 'Jayden']
在上面的示例中,people[*]
代表people下所有的元素,people[*].first
代表people下所有的元素中獲取key為first的元素值,結果被收集到一個JSON陣列中,並作為表示式的結果返回
雖然people陣列中有4個元素,但是最後一個{"missing": "different"}
的值為null,並不會將null值新增到收集的結果陣列中
還有,列表投影僅對列表有效,如果值不是列表,比如是物件,那麼表示式的結果為null
import jmespath
dic_1 = {
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
path = jmespath.search("foo[*]", dic_1)
print(path)
# 結果
None
以上程式碼foo
是一個物件,並不是列表,所以這裡返回的是None
切片投影
切片投影幾乎與列表投影相同,但左側是評估切片的結果,該切片可能未包括原始列表中的所有元素
import jmespath
dic_1 = {
"people": [
{"first": "James", "last": "d"},
{"first": "Jacob", "last": "e"},
{"first": "Jayden", "last": "f"},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
path = jmespath.search("people[:2].first", dic_1)
print(path)
# 結果
['James', 'Jacob']
這裡是先從people陣列中取出前二個變為[{"first": "James", "last": "d"},{"first": "Jacob", "last": "e"}]
,然後再取出欄位為first的元素值
物件投影
列表投影是為JSON陣列
定義的,而物件投影是為JSON物件
定義的。可以使用*語法建立物件投影。這將建立JSON物件的值列表,並將投影的右側投影到值列表上。
import jmespath
dic_1 = {
"ops": {
"functionA": {"numArgs": 2},
"functionB": {"numArgs": 3},
"functionC": {"variadic": True}
}
}
path = jmespath.search("ops.*.numArgs", dic_1)
print(path)
# 結果
[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]
。
展平投影
JMESPath表示式中可以使用多個投影。在列表/物件投影的情況下,在投影中建立投影時保留原始文件的結構。
例如,讓我們以表示式reservations[*].instances[*].state
為例。這個表示式表示頂級鍵保留有一個陣列作為值。對於每個陣列元素,投影例項[*].state
表示式。在每個列表元素中,有一個例項鍵,它本身就是一個值,我們為列表中的每個列表元素建立一個子投影。下面是一個例子:
import jmespath
dic_1 = {
"reservations": [
{
"instances": [
{"state": "running"},
{"state": "stopped"}
]
},
{
"instances": [
{"state": "terminated"},
{"state": "running"}
]
}
]
}
path = jmespath.search("reservations[*].instances[*].state", dic_1)
print(path)
# 結果
[['running', 'stopped'], ['terminated', 'running']]
此表示式的結果是[[“running”,“stopped”],[“terminated”,“running”]]
,其實最外層的[] 就是 reservations[*]
建立的,而內部的每一個例項instances[*]
,也會各自再建立出投影列表,所以結果中最外層的[]裡包含了2個子元素[]。
如果我們只需要一個例項所有狀態的列表呢?理想情況下,我們希望得到一個結果[“running”,“stopped”,“terminated”,“running”]
。在這種情況下,我們不關心例項屬於哪個保留,我們只需要一個狀態列表。
我們可以使用[]而不是[*]來展平列表,表示式:reservations[].instances[].state
import jmespath
dic_1 = {
"reservations": [
{
"instances": [
{"state": "running"},
{"state": "stopped"}
]
},
{
"instances": [
{"state": "terminated"},
{"state": "running"}
]
}
]
}
path = jmespath.search("reservations[].instances[].state", dic_1)
print(path)
# 結果
['running', 'stopped', 'terminated', 'running']
總結:
- 它將子列表展平到父列表中(不是遞迴的,只是一個級別)。
- 它會建立一個投影,因此展平投影右側的任何內容都會投影到新建立的展平列表中。
您也可以單獨使用[]來展平列表:
import jmespath
dic_1 = [
[0, 1],
2,
[3],
4,
[5, [6, 7]]
]
path = jmespath.search("[]", dic_1)
print(path)
# 結果
[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
dic_1 = [
[0, 1],
2,
[3],
4,
[5, [6, 7]]
]
path = jmespath.search("[][]", dic_1)
print(path)
# 結果
[0, 1, 2, 3, 4, 5, 6, 7]