Przewodnik zapytań JSONPath: Jak przeszukiwać dane JSON
JSON stał się uniwersalnym językiem sieci Web. API zwracają JSON, pliki konfiguracyjne używają JSON, bazy danych przechowują dokumenty JSON. Ale w miarę jak struktury JSON stają się większe i głębiej zagnieżdżone, znajdowanie określonych wartości staje się coraz trudniejsze. Tutaj wchodzi JSONPath. JSONPath to język zapytań, który pozwala nawigować i wyodrębniać dane z dokumentów JSON za pomocą zwięzłych wyrażeń ścieżkowych, podobnie jak XPath robi to dla XML. Ten przewodnik obejmuje wszystko, od podstawowej składni po zaawansowane filtrowanie, z praktycznymi przykładami, które możesz zastosować od razu.
Czym jest JSONPath?
JSONPath to język zapytań JSON, pierwotnie zaproponowany przez Stefana Goessnera w 2007 roku. Oferuje zwartą składnię do wybierania węzłów z dokumentu JSON, podobnie jak selektory CSS lokalizują elementy HTML lub wyrażenia XPath lokalizują węzły XML. Zamiast pisać pętle i logikę warunkową do przeglądania struktury JSON, po prostu piszesz wyrażenie opisujące ścieżkę do pożądanych danych.
Wyrażenia JSONPath zaczynają się od węzła głównego dokumentu JSON i nawigują przez obiekty i tablice do pożądanych wartości. Język obsługuje symbole wieloznaczne, zejście rekurencyjne, wycinki tablic i wyrażenia filtrujące, co jest wystarczające dla większości potrzeb ekstrakcji danych.
Przykładowy dokument JSON
W tym przewodniku użyjemy następującego dokumentu JSON jako przykładu roboczego. Jest to klasyczny przykład z oryginalnej propozycji JSONPath z dodatkowymi danymi:
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J.R.R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}Referencja składni JSONPath
JSONPath używa niewielkiego zestawu operatorów, które łączą się, tworząc potężne zapytania. Oto kompletna referencja składni:
| Operator | Opis | Przykład |
|---|---|---|
| $ | Węzeł główny dokumentu | $ |
| . | Operator dziecka (dostęp do właściwości obiektu) | $.store |
| [] | Indeks tablicy lub operator dziecka | $.store.book[0] |
| [*] | Symbol wieloznaczny dla wszystkich elementów tablicy | $.store.book[*] |
| .. | Zejście rekurencyjne (przeszukuje wszystkie poziomy) | $..author |
| .key | Nazwane właściwości obiektu | $.store.bicycle |
| ['key'] | Notacja nawiasowa dla dostępu do właściwości | $['store']['book'] |
| [start:end] | Wycinanie tablicy (indeks końcowy wyłączony) | $.store.book[0:2] |
| [?()] | Wyrażenia filtrujące | < 10)] |
| () | Wyrażenia skryptowe (zależne od implementacji) | $.store.book[(@.length-1)] |
Podstawowe wyrażenia JSONPath
Dostęp do węzła głównego i bezpośrednich dzieci
Znak dolara $ reprezentuje węzeł główny dokumentu JSON. Stamtąd używasz notacji kropkowej, aby uzyskać dostęp do właściwości obiektu, i notacji nawiasowej, aby uzyskać dostęp do indeksów tablicy.
| Wyrażenie | Wynik |
|---|---|
| $ | Cały dokument JSON |
| $.store | obiekt store (zawierający tablicę book i obiekt bicycle) |
| $.store.bicycle | {"color": "red", "price": 19.95} |
| $.store.bicycle.color | "red" |
| $.store.book | Cała tablica book |
| $.store.book[0] | Pierwszy obiekt książki |
| $.store.book[0].title | "Sayings of the Century" |
| $.store.book[3].author | "J.R.R. Tolkien" |
Notacja nawiasowa
Notacja nawiasowa jest alternatywą dla notacji kropkowej, przydatną, gdy nazwy właściwości zawierają znaki specjalne, spacje lub zaczynają się od cyfr:
$.store['book'][0]['title']
$['store']['bicycle']['color']Notacja nawiasowa i notacja kropkowa są wymienne dla właściwości obiektów. Jednak notacja nawiasowa musi być używana, gdy nazwa właściwości jest dynamiczna lub zawiera znaki, które są nieprawidłowe w notacji kropkowej.
Operator wieloznaczny
Operator wieloznaczny * dopasowuje wszystkie elementy w tablicy lub wszystkie właściwości w obiekcie. Jest niezwykle przydatny do wyodrębniania wszystkich wartości na danym poziomie bez znajomości konkretnych kluczy lub indeksów.
| Wyrażenie | Wynik |
|---|---|
| $.store.* | Wszystkie wartości w obiekcie store (tablica book i obiekt bicycle) |
| $.store.book[*] | Wszystkie książki w tablicy (wszystkie cztery obiekty book) |
| $.store.book[*].author | Wszystkie nazwiska autorów: ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J.R.R. Tolkien"] |
| $.store.*.price | cena bicycle i tablica book (nie cena pojedynczej książki) |
Zejście rekurencyjne: operator podwójnej kropki
Operator podwójnej kropki .. jest jedną z najpotężniejszych funkcji JSONPath. Przeszukuje każdy poziom drzewa JSON w poszukiwaniu określonego klucza, nie tylko bezpośrednich dzieci. Pomyśl o tym jak o głębokim wyszukiwaniu w całym dokumencie.
| Wyrażenie | Wynik |
|---|---|
| $..author | Wartości author we wszystkich miejscach dokumentu |
| $..price | Wszystkie wartości price: [8.95, 12.99, 8.99, 22.99, 19.95] |
| $..book[2] | Trzecia książka, wyszukiwanie rekurencyjne |
| $..category | Wszystkie wartości category: ["reference", "fiction", "fiction", "fiction"] |
Operator zejścia rekurencyjnego jest szczególnie przydatny, gdy nie znasz dokładnej ścieżki do żądanych danych lub gdy ten sam klucz pojawia się na wielu poziomach zagnieżdżenia. Na przykład, jeśli odpowiedź API zawiera pole id na różnych poziomach zagnieżdżenia, $..id zbierze wszystkie id.
Wycinanie tablicy
Wycinanie tablicy pozwala wybrać zakres elementów z tablicy. Składnia [start:end] wybiera elementy od indeksu początkowego do (ale nie włączając) indeksu końcowego. Obsługiwane są indeksy dodatnie i ujemne.
| Wyrażenie | Wynik |
|---|---|
| $.store.book[0:2] | Pierwsze dwie książki (indeksy 0 i 1) |
| $.store.book[1:3] | Druga i trzecia książka (indeksy 1 i 2) |
| $.store.book[-1] | Ostatnia książka (The Lord of the Rings) |
| $.store.book[-2:] | Dwie ostatnie książki |
| $.store.book[:2] | Pierwsze dwie książki (tak samo jak [0:2]) |
| $.store.book[2:] | Trzecia książka i kolejne |
Uwaga: zachowanie wycinania może się nieznacznie różnić między implementacjami JSONPath. Powyższa składnia jest zgodna z konwencją używaną przez większość popularnych bibliotek i standard IETF RFC 9535.
Wyrażenia filtrujące
Wyrażenia filtrujące są miejscem, gdzie JSONPath naprawdę staje się potężny. Pozwalają wybierać elementy na podstawie warunków, a nie pozycji. Składnia filtrowania używa [?(warunek)], gdzie warunek jest oceniany dla każdego elementu.
Operatory porównania
JSONPath obsługuje następujące operatory porównania w wyrażeniach filtrujących:
| Operator | Znaczenie | Przykład |
|---|---|---|
| == | Równy | [?(@.category == "fiction")] |
| != | Nierówny | [?(@.category != "fiction")] |
| < | Mniejszy niż | < 10)] |
| <= | Mniejszy lub równy | <= 8.99)] |
| > | Większy niż | [?(@.price > 15)] |
| >= | Większy lub równy | [?(@.price >= 12.99)] |
| =~ | Dopasowanie wyrażeń regularnych (częściowa implementacja) | [?(@.author =~ /Tolkien/i)] |
Praktyczne przykłady filtrowania
Korzystając z naszego przykładowego dokumentu, oto praktyczne wyrażenia filtrujące i ich wyniki:
Find all books cheaper than $10:
< 10)]Zwraca książki "Sayings of the Century" (8,95 USD) i "Moby Dick" (8,99 USD).
Find all fiction books:
$.store.book[?(@.category == "fiction")]Zwraca trzy książki: "Sword of Honour", "Moby Dick" i "The Lord of the Rings".
Find books with an ISBN:
$.store.book[?(@.isbn)]Zwraca książki, które mają właściwość isbn: "Moby Dick" i "The Lord of the Rings". Dzieje się tak, ponieważ filtr sprawdza, czy właściwość istnieje.
Find the most expensive book:
$.store.book[?(@.price > 20)]Zwraca "The Lord of the Rings" (22,99 USD).
Operatory logiczne w filtrach
Możesz łączyć warunki za pomocą operatorów logicznych:
< 10 && @.category == "fiction")]
$.store.book[?(@.price > 15 || @.category == "reference")]Pierwsze wyrażenie znajduje książki beletrystyczne poniżej 10 USD (tylko "Moby Dick"). Drugie znajduje książki powyżej 15 USD lub należące do kategorii referencyjnej (zwraca "Sayings of the Century" i "The Lord of the Rings").
JSONPath a XPath
JSONPath jest wyraźnie wzorowany na XPath, a oba języki mają wiele podobieństw koncepcyjnych. Jeśli znasz przetwarzanie XML, zrozumienie tej relacji może być pomocne.
| Funkcja | JSONPath | XPath |
|---|---|---|
| Symbol pierwiastka | $ | / |
| Dostęp do dzieci | .key lub ['key'] | /element |
| Indeksowanie tablicy | [0] | [1] (zaczynając od 1) |
| Symbol wieloznaczny | * | * |
| Zejście rekurencyjne | .. | // |
| Filtrowanie | [?(condition)] | [condition] |
| Właściwość | Nie dotyczy (JSON nie ma atrybutów) | @attr |
| Bieżący węzeł | @ (w filtrach) | . lub current() |
| Rodzic | Nieobsługiwane | .. |
| Oś | Nieobsługiwane | 13 osi (ancestor, following itp.) |
| Model danych | Obiekty i tablice | Elementy, atrybuty, węzły tekstowe |
Kluczowa różnica polega na tym, że XPath operuje na bogatym modelu drzewa z elementami, atrybutami, węzłami tekstowymi, przestrzeniami nazw i instrukcjami przetwarzania. JSONPath operuje na prostszym modelu obiektów (map klucz-wartość) i tablic (uporządkowanych list). Ta prostota sprawia, że JSONPath jest łatwiejszy do nauki, ale mniej ekspresyjny niż XPath w złożonych zapytaniach.
JSONPath w praktyce
Testowanie API
JSONPath jest niezbędny do testowania API. Gdy wysyłasz żądanie do API i otrzymujesz dużą odpowiedź JSON, JSONPath pozwala na asercję określonych wartości bez ręcznego nawigowania po całej strukturze. Większość narzędzi do testowania API natywnie obsługuje JSONPath.
Na przykład w testach możesz zweryfikować, że cena pierwszej książki jest poniżej 10 dolarów:
// Using a JSONPath assertion in testing
response.jsonPath().get("store.book[0].price").should(equals(8.95));
// Find all books by a specific author
response.jsonPath().get("store.book[?(@.author == 'Herman Melville')].title");
// Returns: ["Moby Dick"]Transformacja danych
Podczas integracji systemów używających różnych formatów danych JSONPath pomaga wyodrębniać i przekształcać określone pola. Możesz wyodrębnić wartości z jednej struktury JSON i mapować je na inną bez pisania złożonego kodu przechodzenia.
Zarządzanie konfiguracją
Złożone pliki konfiguracyjne często zawierają głęboko zagnieżdżony JSON. JSONPath pozwala wysyłać zapytania o konkretne wartości konfiguracyjne bez ładowania i parsowania całej struktury. Narzędzia takie jak jq używają składni podobnej do JSONPath do przetwarzania JSON w potokach wiersza poleceń.
Monitorowanie i alerty
W systemach obserwowalności zapytania JSONPath mogą wyodrębniać metryki z logów i odpowiedzi API w formacie JSON. Możesz skonfigurować alerty, które wyzwalają się, gdy zapytanie JSONPath zwraca wartości przekraczające próg.
Implementacje JSONPath
JSONPath jest dostępny w prawie każdym języku programowania. Oto najpopularniejsze biblioteki:
| Język | Biblioteki | Instalacja |
|---|---|---|
| JavaScript | jsonpath-plus | npm install jsonpath-plus |
| Python | jsonpath-ng | pip install jsonpath-ng |
| Java | JsonPath (Jayway) | Maven: com.jayway.jsonpath |
| C# | Json.NET (Newtonsoft) | NuGet: Newtonsoft.Json |
| Go | gjson | go get github.com/tidwall/gjson |
| PHP | jsonpath | composer require softcreatr/jsonpath |
| Ruby | jsonpath | gem install jsonpath |
Przykład JavaScript
< 10)]',
json: data
});
// Returns books with price < 10
Python Example
from jsonpath_ng import parse
data = { /* our sample JSON */ }
# Find all authors
author_expr = parse('$..author')
authors = [match.value for match in author_expr.find(data)]
# ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J.R.R. Tolkien"]
# Find cheap books
cheap_expr = parse('$.store.book[?(@.price < 10)]')
cheap_books = [match.value for match in cheap_expr.find(data)]
Common Pitfalls and Tips
- Zero-based vs one-based indexing: JSONPath uses zero-based indexing (the first element is [0]), while XPath uses one-based indexing (the first element is [1]). This is a common source of off-by-one errors.
- Implementation differences: Before the IETF standard (RFC 9535), JSONPath implementations varied in their handling of edge cases like empty results, null values, and filter syntax. Always test your expressions with the specific library you are using.
- Filter performance: Recursive descent with filters (
$..book[?(@.price < 10)]) can be slow on large documents because it must traverse the entire tree. For performance-critical applications, use more specific paths when possible. - Case sensitivity: JSONPath is case-sensitive.
$.Storewill not match$.store. This is consistent with JSON's case-sensitive nature. - Indeksowanie od zera a od jedynki: JSONPath używa indeksowania od zera (pierwszy element to [0]), podczas gdy XPath używa indeksowania od jedynki (pierwszy element to [1]). Jest to częste źródło błędów off-by-one.
- Różnice implementacyjne: przed standardem IETF (RFC 9535) implementacje JSONPath różniły się w obsłudze przypadków brzegowych, takich jak puste wyniki, wartości null i składnia filtrowania. Zawsze testuj wyrażenia z konkretną biblioteką.
Typowe pułapki i wskazówki
W 2024 roku IETF opublikował RFC 9535, formalnie standaryzując JSONPath. Specyfikacja rozwiązuje wiele niejasności i niespójności między implementacjami. Kluczowe aspekty standardu obejmują:
- < 10)]) może być wolne na dużych dokumentach, ponieważ musi przejść całe drzewo. W aplikacjach krytycznych pod względem wydajności używaj bardziej konkretnych ścieżek, gdy to możliwe.
- Wrażliwość na wielkość liter: JSONPath rozróżnia wielkość liter. $.Store nie będzie pasować do $.store. Jest to zgodne z wrażliwością na wielkość liter JSON.
- Escapowanie znaków specjalnych: jeśli nazwa klucza zawiera kropki lub nawiasy, musisz użyć notacji nawiasowej z cudzysłowami: $['key.with.dots'] zamiast $.key.with.dots.
- Brak nawigacji do rodzica: w przeciwieństwie do XPath, JSONPath nie może nawigować do węzła nadrzędnego. Nie ma odpowiednika .. (osi rodzica) XPath. .. w JSONPath oznacza zejście rekurencyjne, a nie rodzica.
Jeśli zaczynasz nowy projekt, preferuj biblioteki implementujące RFC 9535 dla maksymalnej kompatybilności i przewidywalnego zachowania.
Potrzebujesz szybko wyszukać dane JSON? Wypróbuj nasz darmowy tester JSONPath online, który ocenia wyrażenia na twoich dokumentach JSON w czasie rzeczywistym.
Wypróbuj wyszukiwarkę JSONPathStandard IETF: RFC 9535
Przykład Python
JSONPath to język zapytań JSON, podobny do XPath dla XML. Używa wyrażeń ścieżkowych do nawigacji i wyodrębniania określonych wartości z dokumentów JSON. Wyrażenia JSONPath, takie jak $.store.book[0].title, pozwalają precyzyjnie lokalizować dane w złożonych, zagnieżdżonych strukturach JSON bez potrzeby pisania niestandardowego kodu parsowania.
Czym jest JSONPath?
JSONPath jest zaprojektowany dla struktur obiektów i tablic JSON, podczas gdy XPath jest zaprojektowany dla drzew elementów i atrybutów XML. JSONPath używa $ jako węzła głównego, notacji kropkowej do dostępu do obiektów i notacji nawiasowej do dostępu do tablic. XPath używa / do separacji ścieżek i @ do dostępu do atrybutów. JSONPath jest prostszy, ale mniej bogaty w funkcje niż XPath.
Czym różni się JSONPath od XPath?
Podwójna kropka (..) w JSONPath to operator zejścia rekurencyjnego. Przeszukuje wszystkie poziomy struktury JSON w poszukiwaniu nazwanego klucza, nie tylko bezpośrednie węzły potomne. Na przykład $..author znajduje wszystkie klucze author w dowolnym miejscu całego dokumentu, niezależnie od głębokości zagnieżdżenia.
Co oznacza podwójna kropka (..) w JSONPath?
<, ><=, >< 10)] znajduje wszystkie książki poniżej 10 dolarów.
Czy JSONPath może filtrować dane na podstawie warunków?
JSONPath został pierwotnie zaproponowany przez Stefana Goessnera w 2007 roku bez formalnej specyfikacji, co prowadziło do różnic między implementacjami. W 2024 roku IETF opublikował RFC 9535, formalnie standaryzując JSONPath. Nowoczesne implementacje zbliżają się do tego standardu, ale niektóre starsze biblioteki mogą nadal mieć niewielkie różnice składniowe.