jmespath(2)投影Projections

Silent丿丶黑羽發表於2021-02-08

投影

投影是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]

相關文章