JSON查詢語言——JMESPath

簡介

JMESPath是JSON的查詢語言,特點:

  • 完整規範
    JMESPath語言是由完整規範的ABNF語法描述的,確保了精確定義語言語法
  • 遵循性測試套件
    JMESPath有一整套數據驅動的測試用例,確保多個庫的一致性
  • 多語言庫
    每個JMESPath庫都通過了一整套遵循性測試,有多種語言,包括Python、PHP、JavaScript和Lua

在這裏插入圖片描述




安裝

pip install jmespath

本人開發了GUI方便學習——代碼地址

在這裏插入圖片描述




API

Python 的 jmespath 庫提供了兩個解析操作:

def search(expression, data, options=None)

import jmespath

path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
print(path)
# baz



def compile(expression)

類似re模塊,可以使用 compile() 編譯JMESPath表達式,並執行重複搜索

import jmespath

expression = jmespath.compile('foo.bar')
print(expression.search({'foo': {'bar': 'baz'}}))
print(expression.search({'foo': {'bar': 'other'}}))
# baz
# other




基本表達式

最簡單的JMESPath表達式是標識符,在JSON對象中選擇一個鍵:

取鍵a

expression

a

value

{"a": "foo", "b": "bar", "c": "baz"}

result

"foo"




若鍵不存在,則返回null

import jmespath

expression = jmespath.compile('d')  # 選擇鍵a
value = {"a": "foo", "b": "bar", "c": "baz"}
print(expression.search(value))
# None




子表達式返回JSON的嵌套值

expression

a.b.c.d

value

{"a": {"b": {"c": {"d": "value"}}}}

result

"value"




索引表達式,類似數組訪問,從0開始

expression

[1]

value

["a", "b", "c", "d", "e", "f"]

result

"b"

從後面開始數,如[-1]、[-2]

expression

[-2]

result

"e"




組合標識符、子表達式和索引表達式

expression

a.b.c[0].d[1][0]

value

{"a": {
  "b": {
    "c": [
      {"d": [0, [1, 2]]},
      {"d": [3, 4]}
    ]
  }
}}

result

1




切片

切片可以獲得數組的子集:

  • 左閉右開
  • 切片的三個參數[起始位置:終止位置:步長]




取數組前五個元素

expression爲[0:5]或[:5]一樣

[:5]

value

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

result

[
  0,
  1,
  2,
  3,
  4
]




間隔爲2取數組元素

expression

[::2]

result

[
  0,
  2,
  4,
  6,
  8
]




逆序

expression

[::-1]

result

[
  9,
  8,
  7,
  6,
  5,
  4,
  3,
  2,
  1,
  0
]




投影

投影是JMESPath的關鍵特性之一,有五種投影:

  • 列表投影
  • 切片投影
  • 對象投影
  • 扁平投影
  • 過濾投影

列表和切片投影

通配符 * 創建列表投影

取鍵people,取所有元素,取鍵first

expression

people[*].first

value

{
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ],
  "foo": {"bar": "baz"}
}

result

[
  "James",
  "Jacob",
  "Jayden"
]




切片投影,取前兩個元素,取鍵first

expression

people[:2].first

result

[
  "James",
  "Jacob"
]

對象投影

同樣可以使用通配符 *




取鍵ops,取所有鍵,取鍵numArgs

expression

ops.*.numArgs

value

{
  "ops": {
    "functionA": {"numArgs": 2},
    "functionB": {"numArgs": 3},
    "functionC": {"variadic": true}
  }
}

result

[
  2,
  3
]

扁平投影

扁平化操作符 [] 將當前結果中的子列表合併爲單個列表,操作如下:

  • 創建一個空的結果列表
  • 遍歷當前結果元素
  • 如果當前元素不是列表,則將其添加到結果列表的末尾
  • 如果當前元素是列表,則將當前元素的每個元素添加到結果列表的末尾




只用列表投影,結果不是扁平的

expression

reservations[*].instances[*].state

value

{
  "reservations": [
    {
      "instances": [
        {"state": "running"},
        {"state": "stopped"}
      ]
    },
    {
      "instances": [
        {"state": "terminated"},
        {"state": "running"}
      ]
    }
  ]
}

result

[
  [
    "running",
    "stopped"
  ],
  [
    "terminated",
    "running"
  ]
]




使用扁平投影 []

expression

reservations[].instances[].state

result

[
  "running",
  "stopped",
  "terminated",
  "running"
]




嵌套列表拆開一層

expression

[]

value

[
  [0, 1],
  2,
  [3],
  4,
  [5, [6, 7]]
]

result

[
  0,
  1,
  2,
  3,
  4,
  5,
  [
    6,
    7
  ]
]




拆開兩層

expression

[][]

result

[
  0,
  1,
  2,
  3,
  4,
  5,
  6,
  7
]

過濾投影

類似判斷語句,操作符 ? ,語法:[? <expression> <comparator> <expression>]

比較符號有:

  • ==
  • !=
  • <
  • <=
  • >
  • >=




只取state爲running的

expression

machines[?state=='running'].name

value

{
  "machines": [
    {"name": "a", "state": "running"},
    {"name": "b", "state": "stopped"},
    {"name": "b", "state": "running"}
  ]
}

result

[
  "a",
  "b"
]




管表達式

對投影的結果進行操作




取鍵people的所有元素,取鍵first,取結果的第一個元素

expression

people[*].first | [0]

value

{
  "people": [
    {"first": "James", "last": "d"},
    {"first": "Jacob", "last": "e"},
    {"first": "Jayden", "last": "f"},
    {"missing": "different"}
  ],
  "foo": {"bar": "baz"}
}

result

"James"




多選(新數據)

創建JSON元素


多選列表

expression

people[].[name, state.name]

value

{
  "people": [
    {
      "name": "a",
      "state": {"name": "up"}
    },
    {
      "name": "b",
      "state": {"name": "down"}
    },
    {
      "name": "c",
      "state": {"name": "up"}
    }
  ]
}

result

[
  [
    "a",
    "up"
  ],
  [
    "b",
    "down"
  ],
  [
    "c",
    "up"
  ]
]




加上鍵

expression

people[].{Name: name, State: state.name}

result

[
  {
    "Name": "a",
    "State": "up"
  },
  {
    "Name": "b",
    "State": "down"
  },
  {
    "Name": "c",
    "State": "up"
  }
]




函數

JMESPath支持函數表達式,請查閱:

長度

expression

length(people)

value

{
  "people": [
    {
      "name": "b",
      "age": 30,
      "state": {"name": "up"}
    },
    {
      "name": "a",
      "age": 50,
      "state": {"name": "down"}
    },
    {
      "name": "c",
      "age": 40,
      "state": {"name": "up"}
    }
  ]
}

result

3




輸出年紀最大的人

expression

max_by(people, &age).name

value

{
  "people": [
    {
      "name": "b",
      "age": 30
    },
    {
      "name": "a",
      "age": 50
    },
    {
      "name": "c",
      "age": 40
    }
  ]
}

result

"a"




函數與過濾結合使用

expression

myarray[?contains(@, 'foo') == `true`]

value

{
  "myarray": [
    "foo",
    "foobar",
    "barfoo",
    "bar",
    "baz",
    "barbaz",
    "barfoobaz"
  ]
}

result

[
  "foo",
  "foobar",
  "barfoo",
  "barfoobaz"
]




例子

篩選出20歲以上的人,要姓名和年齡

expression

people[?age > `20`].{name:name, age:age}

value

{
  "people": [
    {
      "age": 20,
      "other": "foo",
      "name": "Bob"
    },
    {
      "age": 25,
      "other": "bar",
      "name": "Fred"
    },
    {
      "age": 30,
      "other": "baz",
      "name": "George"
    }
  ]
}

result

[
  {
    "name": "Fred",
    "age": 25
  },
  {
    "name": "George",
    "age": 30
  }
]




函數封裝

import json
import jmespath


def parse(expression, value):
    '''使用JMESPath解析JSON

    :param expression: JMESPath字符串
    :param value: JSON字符串
    :return: 解析結果

    >>> parse('a', '{"a": "foo", "b": "bar", "c": "baz"}')  # 基本表達式
    'foo'
    >>> parse('[:5]', '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]')  # 切片
    [0, 1, 2, 3, 4]
    >>> parse("machines[?state=='running'].name", '{"machines": [{"name": "a", "state": "running"}, {"name": "b", "state": "stopped"}]}')  # 投影
    ['a']
    >>> parse('people[*].first | [0]', '{"people": [{"first": "James"}, {"first": "Jacob"}]}')  # 管表達式
    'James'
    >>> parse('people[].{id: name}', '{"people": [{"name": "James"}, {"name": "Jacob"}]}')  # 多選
    [{'id': 'James'}, {'id': 'Jacob'}]
    >>> parse('length(people)', '{"people": [{"name": "James"}, {"name": "Jacob"}]}')  # 函數表達式
    2
    '''
    try:
        expression = jmespath.compile(expression)
        value = json.loads(value)
        result = expression.search(value)
    except Exception as e:
        return None
    else:
        return result




參考文獻

  1. JMESPath 官網
  2. JMESPath 教程
  3. JMESPath 例子
  4. JMESPath 表達式
  5. jmespath.py: JMESPath is a query language for JSON.
  6. RFC4234 定義的 ABNF
  7. 語法規範:BNF與ABNF
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章