JMESPath dla początkujących: zapytania projekcyjne lepsze niż JSONPath
Podsumowanie: JMESPath przekształca partie obiektów bardziej bezpośrednio niż JSONPath. Ten przewodnik dla początkujących omawia składnię, projekcje, potoki, wbudowane funkcje i użycie w AWS CLI.

Najważniejsze: używaj JSON Pointer do lokalizowania po pozycji, JSONPath do filtrowania lub rekurencji, a JMESPath, gdy musisz przekształcić wynik albo zaprojektować go w wierszu poleceń; pojedyncze znane pole nie potrzebuje żadnego z nich.
Jeśli twoim celem jest przekształcenie partii obiektów w inny kształt, JMESPath jest często bardziej bezpośredni niż JSONPath. Nie szuka węzłów; konstruuje wynik.
1. Czym jest JMESPath
JMESPath to język zapytań zaprojektowany specjalnie dla JSON. Powstał pierwotnie do filtrowania wyjścia AWS CLI, a później rozprzestrzenił się na wiele języków przez implementacje takie jak jmespath.js, jmespath.py i go-jmespath. Jego cel różni się od JSONPath:
- JSONPath skupia się na wybieraniu zbioru węzłów po ścieżce i warunku i zwraca zbiór węzłów;
- JMESPath skupia się na przekształcaniu wejściowego JSON-a i zwraca wartość o dowolnym kształcie.
Ta różnica czyni JMESPath szczególnie dobrym do projekcji list, zmiany nazw pól i przycinania wyników, na przykład redukcji dwudziestu pól z odpowiedzi API do trzech pól, których frontend naprawdę potrzebuje. Jeśli nie czytałeś jeszcze podstaw JSONPath, zacznij od Języka zapytań JSONPath w praktyce.
2. Podstawowa składnia: od identyfikatorów do indeksów
JMESPath używa mniej symboli niż JSONPath i czyta się bardziej jak wyrażenie ekstrakcji:
| Składnia | Znaczenie | Przykład |
|---|---|---|
name |
Dostęp do składowej obiektu | people |
a.b |
Dostęp do zagnieżdżonych składowych | store.bicycle.color |
[0] |
Dostęp do indeksu tablicy | people[0] |
[-1] |
Dostęp do ostatniego elementu | people[-1] |
[0:2] |
Wycinek, początek włącznie, koniec wyłącznie | people[0:2] |
[*] |
Projekcja listy | people[*].name |
[] |
Projekcja spłaszczająca | people[].tags[] |
[?condition] |
Projekcja filtrująca | people[?age > 18] |
{key: expr} |
Wieloselekcyjny hash | {name: name, age: age} |
[expr, expr] |
Wieloselekcyjna lista | [name, age] |
| |
Przekazanie wyniku lewej strony do prawej | people[*].age | [0] |
Minimalny przykład:
{
"people": [
{ "name": "Alice", "age": 31, "city": "Shanghai" },
{ "name": "Bob", "age": 17, "city": "Beijing" }
]
}
people[0].name → "Alice"
people[-1].city → "Beijing"
people[0:2].name → ["Alice", "Bob"]
Zauważ, dlaczego people[0:2].name zwraca tablicę: wycinek zwraca listę, a następujące .name jest automatycznie projektowane na każdy element tej listy. To największa różnica między JMESPath a zwykłym dostępem do obiektu.
3. Projekcje: zamiana jednej listy w drugą
Projekcja to kluczowe pojęcie w JMESPath. Działa w dwóch krokach: oceń każdy element listy, a następnie zbierz wyniki w nową listę. Elementy, które oceniają się do null, są automatycznie odrzucane.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // a list of objects
people[?age > 18].name // ["Alice"]
Druga linia to typowe wyrażenie projekcyjne: people[*].{name: name, age: age}. Odbudowuje każdy element jako { "name": ..., "age": ... } i odrzuca dodatkowe pole city. W porównaniu z JSONPath, który daje węzły, które potem mapujesz w kodzie, to oszczędza pętlę.
Warto rozróżnić dwa rodzaje projekcji:
[*]zachowuje oryginalną strukturę listy i pomija wartości null;[]to projekcja spłaszczająca, która najpierw zwija jeden poziom zagnieżdżonych list, co pasuje do wyrażeń takich jakpeople[].tags[], potrzebujących „wszystkich tagów wszystkich osób”.
4. Potoki i wieloselekcja
Potok | znaczy „zatrzymaj obecną projekcję i potraktuj wynik jako nowe wejście”. Jego najczęstsze użycie to wzięcie indeksu po projekcji:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → the maximum age
Bez potoku people[*].name[0] jest interpretowane jako wzięcie pierwszego znaku każdej nazwy, co nie jest tym, czego chcesz. Potok to najbardziej przeoczany i najbardziej użyteczny symbol w JMESPath.
Istnieją dwie formy wieloselekcji:
// Multi-select hash: the result is an object
people[0].{name: name, age: age}
// Multi-select list: the result is an array
people[0].[name, age]
Wieloselekcyjne hashe są dobre do konstruowania nazwanych wyników, a wieloselekcyjne listy do konsumpcji pozycyjnej. Oba można zagnieżdżać w projekcjach, ale uwaga: w wieloselekcyjnym hashu klucz o wartości null nadal jest zachowywany, podczas gdy projekcja odrzuca cały element null.
5. Wbudowane funkcje
Standard JMESPath definiuje zestaw funkcji. Częste z nich to:
| Funkcja | Cel | Przykład |
|---|---|---|
length() |
Długość | length(people) |
sort() |
Sortowanie wartości | sort(people[*].age) |
sort_by() |
Sortowanie po polu | sort_by(people, &age) |
max() / min() |
Maksimum / minimum | max(people[*].age) |
sum() |
Suma wartości | sum(people[*].age) |
keys() |
Klucze obiektu | keys(people[0]) |
contains() |
Sprawdzenie zawierania | contains(name, "Ali") |
to_string() |
Konwersja do łańcucha | to_string(age) |
Drugi argument sort_by to wyrażenie &, znaczące „weź to pole z każdego elementu”:
sort_by(people, &age)[*].name
Te funkcje pozwalają JMESPath wykonywać prostą agregację, podczas gdy JSONPath jest zaprojektowany tylko do selekcji; sumowanie i grupowanie zwykle wymagają jq lub kodu.
6. Porównanie obok siebie z JSONPath
| Wymiar | JSONPath | JMESPath |
|---|---|---|
| Główny cel | Wybór węzłów | Przekształcanie danych |
| Symbol korzenia | $ |
Brak; pisz nazwy pól bezpośrednio |
| Obecny węzeł | @ |
@ (tylko w argumentach funkcji) |
| Filtr | [?(@.age > 18)] |
[?age > 18] |
| Symbol wieloznaczny | [*] |
[*] lub [] |
| Konstruowanie nowych obiektów | Niewspierane | {name: name} |
| Potoki | Niewspierane | | |
| Standard | RFC 9535 | Specyfikacja JMESPath |
Przewodnik decyzyjny w jednym zdaniu:
- Użyj JSONPath, gdy potrzebujesz tylko znaleźć węzły pasujące do warunku;
- Użyj JMESPath, gdy musisz przekształcić wynik w to, czego oczekuje frontend;
- Nie używaj żadnego z nich, gdy czytasz jedno znane pole; po prostu uzyskaj dostęp do właściwości bezpośrednio.
Jeśli wynik zapytania musi zostać zapisany z powrotem do dokumentu, żaden język nie pasuje. Zamiast tego użyj JSON Pointer i patchy; zobacz JSON Pointer, Patch i Merge Patch.
7. JMESPath w AWS CLI
Parametr --query w AWS CLI to JMESPath, co jest jego najszerzej rozpowszechnionym zastosowaniem:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
To polecenie robi trzy rzeczy: spłaszcza dwie zagnieżdżone listy przez [], zachowuje tylko trzy pola przez wieloselekcyjny hash i renderuje wynik jako tabelę przez --output table. W JSONPath potrzebowałbyś więcej poziomów i nie dostałbyś efektu „zmiana nazwy plus przycięcie”.
Inne częste formy:
# Only running instances
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# The value of the Name tag
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Zauważ, że backticki otaczają literały, a nie łańcuchy. W AWS CLI łańcuchy używają pojedynczych cudzysłowów, a literały JSON używają backticków. Ta różnica to jeden z najczęstszych błędów początkujących.
8. Częste błędy
- Mylenie
[*]i[]: pierwszy zachowuje zagnieżdżenie, drugi spłaszcza jeden poziom, a wynikowe struktury są zupełnie różne; - Zapominanie potoku:
people[*].name[0]bierze pierwszy znak każdej nazwy, a nie pierwszą nazwę; - Pisanie
@wewnątrz filtra: filtry JMESPath używają nazw pól bezpośrednio, więc[?@.age > 18]jest błędne; - Traktowanie
nulljak pustego łańcucha: projekcje odrzucająnull, ale wieloselekcyjne hashe zachowują klucz; - Używanie podwójnych cudzysłowów wokół literałów w AWS CLI: powłoka najpierw je rozwija i przepisuje wyrażenie.
Rada debugowania: zwaliduj wyrażenie na najmniejszym możliwym wejściu, zanim zastosujesz je do prawdziwych danych, i dodaj --output json w AWS CLI, aby zobaczyć surowe wyniki. Do codziennej edycji i formatowania JSON połączenie z Edytorami JSON i narzędziami wiersza poleceń znacznie przyspiesza lokalizowanie ścieżek pól.
FAQ
Czy JMESPath i JSONPath można używać razem?
Tak, ale zwykle jest to niepotrzebne. Częste podejście to przycięcie małego obiektu przez JMESPath, a potem zwykły dostęp do właściwości w kodzie. Jeśli naprawdę musisz lokalizować po ścieżce i modyfikować, JSONPath czyta, a JSON Pointer pisze, co jest jaśniejszym podziałem pracy.
Dlaczego people[*].name zwraca coś innego niż pojedynczy łańcuch?
Bo [*] to projekcja, a wynik zawsze jest listą, nawet gdy jest tylko jedna osoba. Aby uzyskać pojedynczą wartość, dodaj potok i indeks: people[*].name | [0].
Co się dzieje z null wewnątrz projekcji?
Projekcja listy automatycznie pomija elementy, które oceniają się do null, więc wynik może być krótszy niż oryginalna lista. Wieloselekcyjny hash zachowuje klucz i wartość null, co ma znaczenie przy konstruowaniu odpowiedzi API.
Czy JMESPath potrafi agregować i grupować?
Potrafi prostą agregację przez sum(), max() i sort_by(). Grupowanie, łączenia i funkcje okna są poza standardem. Złożoną analizę zostaw jq, SQL lub kodowi.
Jak zdecydować między JMESPath a JSONPath?
Użyj JSONPath, gdy musisz wybierać węzły po ścieżce i warunku i otrzymać zbiór węzłów; użyj JMESPath, gdy musisz przekształcić wynik w to, czego oczekuje frontend, ze zmianą nazw i przycinaniem pól. Gdy czytasz jedno znane pole, nie używaj żadnego z nich; po prostu uzyskaj dostęp do właściwości.
Czy użyć potoku, aby wziąć pierwszą zaprojektowaną wartość?
Tak. people[*].name to projekcja, więc wynik zawsze jest listą; bez potoku people[*].name[0] bierze pierwszy znak każdej nazwy. Napisz people[*].name | [0], aby uzyskać pierwszą wartość.