簡介
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