Cos'è JSONPath?
JSONPath è un linguaggio di query per estrarre valori da documenti JSON. Originariamente proposto da Stefan Goessner nel 2007 come analogo di XPath per XML, JSONPath consente di scrivere espressioni come $.store.book[*].author per selezionare tutti i campi autore all'interno di un array di libri senza scrivere cicli o codice di attraversamento manuale. Il linguaggio è stato standardizzato nel febbraio 2024 come RFC 9535, pubblicato dall'IETF.
Un'espressione JSONPath inizia sempre con $ che rappresenta la radice del documento. Da lì si concatenano i selettori: la notazione a punto per le chiavi degli oggetti, la notazione a parentesi per gli indici degli array, i caratteri jolly per tutti i figli e l'operatore di discesa ricorsiva (..) per cercare in ogni livello di annidamento. Le espressioni filtro come [?(@.price < 10)] permettono di selezionare elementi in base a condizioni valutate sui loro valori.
JSONPath è utilizzato in strumenti di test API come Postman, piattaforme di osservabilità come Datadog e Splunk, motori di workflow come Apache NiFi, e librerie di programmazione in JavaScript, Python, Go, Java e C#. Testare le proprie espressioni su dati reali prima di incorporarle nel codice o nei file di configurazione previene i fallimenti silenziosi quando la struttura JSON non corrisponde alle aspettative.
Perché usare questo Tester JSONPath?
Scrivere espressioni JSONPath a mano è soggetto a errori. Un punto mancante, il tipo sbagliato di parentesi o una sintassi di filtro errata possono restituire risultati vuoti senza alcun messaggio di errore. Questo strumento fornisce un feedback visivo immediato su ciò che la tua espressione corrisponde.
Casi d'Uso di JSONPath
Riferimento della Sintassi JSONPath
RFC 9535 definisce la sintassi standard di JSONPath. La tabella seguente copre gli operatori che userai nella maggior parte delle query. Tutte le espressioni iniziano con $ (il nodo radice) e concatenano uno o più selettori per navigare nella struttura del documento.
| Operatore | Descrizione | Esempio |
|---|---|---|
| $ | 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 e XPath risolvono lo stesso problema (interrogare dati strutturati) per formati e casi d'uso diversi. JSONPath è orientato al JSON ed è disponibile come libreria nella maggior parte dei linguaggi. jq è uno strumento CLI autonomo con un proprio linguaggio di filtri Turing-completo. XPath opera su XML e fa parte dello stack di specifiche W3C.
| Caratteristica | JSONPath | jq | XPath |
|---|---|---|---|
| Formato dati | JSON | JSON | XML |
| Accesso al primo elemento | $.store.book[0] | .store.book[0] | /store/book[1] |
| Ricerca ricorsiva | $..price | .. | .price? | //price |
| Espressione filtro | [?(@.price<10)] | select(.price < 10) | [price<10] |
| Specifica | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
Esempi di Codice
Come valutare espressioni JSONPath nei linguaggi e strumenti più diffusi. Ogni esempio usa la stessa struttura JSON di una libreria per il confronto.
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}