Co je JSONPath?
JSONPath je dotazovací jazyk pro extrakci hodnot z dokumentů JSON. Původně ho navrhl Stefan Goessner v roce 2007 jako analogii k XPath pro XML. JSONPath umožňuje psát výrazy jako $.store.book[*].author a vybírat tak všechna pole author uvnitř pole book bez psaní smyček nebo ručního procházení kódem. Jazyk byl standardizován v únoru 2024 jako RFC 9535, vydaný organizací IETF.
Výraz JSONPath vždy začíná znakem $ reprezentujícím kořen dokumentu. Od něj lze řetězit selektory: tečková notace pro klíče objektu, závorková notace pro indexy pole, zástupné znaky pro všechny potomky a operátor rekurzivního sestupu (..) pro prohledávání každé úrovně vnoření. Výrazy filtru jako [?(@.price < 10)] umožňují vybírat prvky na základě podmínek vyhodnocených oproti jejich hodnotám.
JSONPath se používá v nástrojích pro testování API jako Postman, na observability platformách jako Datadog a Splunk, v workflow enginech jako Apache NiFi a v programovacích knihovnách pro JavaScript, Python, Go, Javu a C#. Testování výrazů oproti reálným datům před jejich vložením do kódu nebo konfiguračních souborů předchází tichým selháním, když struktura JSON neodpovídá předpokladům.
Proč používat tento JSONPath Tester?
Ruční psaní výrazů JSONPath je náchylné k chybám. Chybějící tečka, špatný typ závorek nebo nesprávná syntaxe filtru může vrátit prázdné výsledky bez chybové zprávy. Tento nástroj poskytuje okamžitou vizuální zpětnou vazbu o tom, co váš výraz odpovídá.
Případy použití JSONPath
Přehled syntaxe JSONPath
RFC 9535 definuje standardní syntaxi JSONPath. Níže uvedená tabulka pokrývá operátory, které budete používat ve většině dotazů. Všechny výrazy začínají znakem $ (kořenový uzel) a řetězí jeden nebo více selektorů k procházení strukturou dokumentu.
| Operátor | Popis | Příklad |
|---|---|---|
| $ | Root element | $.store |
| .key | Child property | $.store.book |
| [n] | Array index (zero-based) | $.store.book[0] |
| [*] | All elements in array/object | $.store.book[*] |
| .. | Recursive descent | $..author |
| [start:end] | Array slice | $.store.book[0:2] |
| [?()] | Filter expression | $.store.book[?(@.price<10)] |
| @ | Current node (inside filter) | $.store.book[?(@.isbn)] |
JSONPath vs jq vs XPath
JSONPath, jq a XPath řeší stejný problém (dotazování strukturovaných dat) pro různé formáty a případy použití. JSONPath je zaměřen na JSON a je dostupný jako knihovna ve většině jazyků. jq je samostatný nástroj CLI s vlastním Turingovsky úplným filtrovacím jazykem. XPath pracuje s XML a je součástí specifikačního zásobníku W3C.
| Vlastnost | JSONPath | jq | XPath |
|---|---|---|---|
| Formát dat | JSON | JSON | XML |
| Přístup k prvnímu prvku | $.store.book[0] | .store.book[0] | /store/book[1] |
| Rekurzivní vyhledávání | $..price | .. | .price? | //price |
| Výraz filtru | [?(@.price<10)] | select(.price < 10) | [price<10] |
| Specifikace | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
Příklady kódu
Jak vyhodnocovat výrazy JSONPath v oblíbených jazycích a nástrojích. Každý příklad používá stejnou strukturu JSON knihkupectví pro srovnání.
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
book: [
{ title: 'Moby Dick', price: 8.99 },
{ title: 'The Great Gatsby', price: 12.99 }
]
}
};
// Get all book titles
JSONPath({ path: '$.store.book[*].title', json: data });
// → ['Moby Dick', 'The Great Gatsby']
// Recursive descent — find every price in the document
JSONPath({ path: '$..price', json: data });
// → [8.99, 12.99]
// Filter — books under $10
JSONPath({ path: '$.store.book[?(@.price < 10)]', json: data });
// → [{ title: 'Moby Dick', price: 8.99 }]from jsonpath_ng.ext import parse
import json
data = {
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}
# Get all book titles
expr = parse("$.store.book[*].title")
titles = [match.value for match in expr.find(data)]
# → ['Moby Dick', 'The Great Gatsby']
# Recursive descent — all prices
expr = parse("$..price")
prices = [match.value for match in expr.find(data)]
# → [8.99, 12.99]
# Filter — books under $10
expr = parse("$.store.book[?price < 10]")
cheap = [match.value for match in expr.find(data)]
# → [{"title": "Moby Dick", "price": 8.99}]package main
import (
"fmt"
"github.com/ohler55/ojg/jp"
"github.com/ohler55/ojg/oj"
)
func main() {
src := `{
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}`
obj, _ := oj.ParseString(src)
// Get all book titles
expr, _ := jp.ParseString("$.store.book[*].title")
results := expr.Get(obj)
fmt.Println(results)
// → [Moby Dick The Great Gatsby]
// Recursive descent — all prices
expr2, _ := jp.ParseString("$..price")
fmt.Println(expr2.Get(obj))
// → [8.99 12.99]
}# jq uses its own syntax, not JSONPath, but solves the same problem.
# Mapping common JSONPath patterns to jq:
# $.store.book[*].title → get all titles
echo '{"store":{"book":[{"title":"Moby Dick"},{"title":"Gatsby"}]}}' | \
jq '.store.book[].title'
# → "Moby Dick"
# → "Gatsby"
# $..price → recursive descent for "price" keys
echo '{"a":{"price":1},"b":{"price":2}}' | \
jq '.. | .price? // empty'
# → 1
# → 2
# Filter: books where price < 10
echo '{"store":{"book":[{"title":"A","price":8},{"title":"B","price":12}]}}' | \
jq '.store.book[] | select(.price < 10)'
# → {"title":"A","price":8}