Skip to content
Torna al blog
Tutorial

Guida alla sintassi JSONPath: interrogare e filtrare JSON con esempi

Impara la sintassi JSONPath con esempi pronti da copiare: radice, discesa ricorsiva, wildcard, slice e filtri. Prova ogni query online nel tuo browser.

11 min di lettura

Guida alla sintassi JSONPath: interrogare e filtrare JSON con esempi

JSONPath è un linguaggio di query per JSON, proprio come XPath lo è per XML. Scrivi un’espressione di percorso e il valutatore restituisce ogni valore che corrisponde. Per raccogliere tutti i nomi degli autori da un documento bookstore scrivi $.store.book[*].author e ti torna la lista degli autori, senza una riga di codice di attraversamento.

Questa guida ti accompagna attraverso ogni selettore della sintassi JSONPath con esempi pronti da copiare che puoi eseguire mentre leggi. Una cosa da chiarire subito: esistono due dialetti. Il dialetto Goessner del 2007 è il classico de facto, mentre RFC 9535 è lo standard IETF formale pubblicato a febbraio 2024. Concordano sui percorsi comuni e divergono nei casi limite, perciò questa guida segnala le differenze man mano che emergono. Puoi provare ogni espressione qui sotto nel JSONPath Tester e passare da un motore all’altro per confrontarli.

Ecco la tabella riassuntiva dei selettori per cominciare. Il resto dell’articolo espande ogni riga con un esempio pratico su un unico documento JSON condiviso.

SelettoreSignificatoEsempio
$Radice del documento$
@Elemento corrente (nei filtri)[?(@.price < 10)]
.name / ['name']Membro figlio$.store.book
..Discesa ricorsiva$..author
*Tutti gli elementi / membri$.store.book[*]
[0]Indice di array$.store.book[0]
[start:end:step]Slice di array (semiaperto)$.store.book[0:2]
[a,b]Unione di nomi / indici$.store.book[0,2]
[?()]Espressione di filtro$.store.book[?(@.price < 10)]
length() count() match() search() value()Funzioni RFC 9535 (solo nei filtri)[?length(@.title) > 15]

Cos’è JSONPath?

JSONPath è un linguaggio di query dichiarativo per selezionare nodi da un documento JSON. Invece di scrivere un ciclo che attraversa oggetti e array, descrivi la posizione che vuoi con un percorso e il valutatore restituisce i valori corrispondenti. Il modello mentale è lo stesso che XPath ti dà per XML: un percorso fatto di selettori che attraversano passo passo la struttura.

Lo trovi ovunque uno sviluppatore tocchi JSON. Lo usi per estrarre un campo da una risposta API, per fare asserzioni su un valore in un test di integrazione, per indirizzare campi nelle configurazioni di pipeline per Kubernetes, AWS Step Functions e Azure Logic Apps, e per estrarre dati da JSON grandi o irregolari senza scrivere a mano la logica di attraversamento.

Una breve nota storica, perché spiega la divisione tra dialetti. Stefan Goessner propose JSONPath nel 2007. Si diffuse in fretta e diventò uno standard de facto, ma non fu mai specificato formalmente, così le implementazioni presero strade diverse sui dettagli. L’IETF ha colmato quel vuoto a febbraio 2024 con RFC 9535, la prima specifica formale di JSONPath. Entrambi i dialetti sono vivi oggi, ed è per questo che la stessa espressione può comportarsi in modo diverso a seconda della libreria che la esegue.

Prima di iniziare a interrogare, conviene leggere la struttura. Formatta input disordinato con il formattatore JSON così l’annidamento diventa visibile.

Il documento di esempio

Ogni esempio qui sotto viene eseguito sul classico JSON bookstore di Goessner. Incollalo una volta e riutilizzalo:

{
  "store": {
    "book": [
      { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
      { "title": "Sword of Honour", "author": "Evelyn Waugh", "price": 12.99 },
      { "title": "Moby Dick", "author": "Herman Melville", "price": 8.99 },
      { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

Quattro libri con titolo, autore e prezzo, più una bicicletta. Tieni a mente: i prezzi sono 8.95, 12.99, 8.99 e 22.99, e sono questi che determinano i risultati dei filtri più avanti.

Radice, figlio e discesa ricorsiva ($ . ..)

Ogni espressione parte dalla radice, scritta $. Da lì entri nei figli con un punto o con la notazione a parentesi, e le due forme sono equivalenti:

$.store.book          → the book array
$['store']['book']    → identical result

La notazione a parentesi è ciò che ti serve quando una chiave contiene spazi, punti o altri caratteri speciali: $['first name'] funziona dove $.first name non funzionerebbe.

L’operatore .. è la discesa ricorsiva. Cerca a ogni livello del documento, non solo i figli diretti, e raccoglie tutto ciò che corrisponde al selettore successivo a qualsiasi profondità:

$..author
→ ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]

Quando usare .. invece di un percorso completo

La discesa ricorsiva è comoda ma grossolana. $..price corrisponde a ogni prezzo ovunque nell’albero, compreso store.bicycle.price, che forse non volevi. Quando conosci la forma, scrivi il percorso per intero così la query resta precisa:

$..price                  → [8.95, 12.99, 8.99, 22.99, 19.95]  (includes the bicycle)
$.store.book[*].price     → [8.95, 12.99, 8.99, 22.99]         (only books)

Riserva .. per strutture davvero irregolari o sconosciute. Lo scambio è comodità contro controllo: più conosci i tuoi dati, più dovresti preferire un percorso esplicito.

Wildcard, indici e slice di array (* [0] [start:end:step])

La wildcard * seleziona tutti gli elementi di un array o tutti i membri di un oggetto:

$.store.book[*].title
→ ["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]

Gli indici di array partono da zero e gli indici negativi contano dalla fine:

$.store.book[0].title     → ["Sayings of the Century"]
$.store.book[-1].title    → ["The Lord of the Rings"]

Le slice usano la stessa convenzione semiaperta [start:end:step] di Python e JavaScript: start è incluso, end è escluso.

$.store.book[0:2].title   → ["Sayings of the Century", "Sword of Honour"]

Questo restituisce due libri, non tre: indice 0 e indice 1, fermandosi prima dell’indice 2. Il limite finale esclusivo è il singolo bug JSONPath più comune, quindi vale la pena imprimerlo nella memoria:

[0:2]   → first TWO elements (indices 0, 1)   ← correct
[0:3]   → first THREE elements (indices 0, 1, 2)

Ometti un limite per arrivare fino al bordo e aggiungi uno step per ogni N-esimo elemento:

$.store.book[2:].title    → ["Moby Dick", "The Lord of the Rings"]
$.store.book[:3].title    → first three titles
$.store.book[::2].title   → ["Sayings of the Century", "Moby Dick"]  (every other)

Espressioni di filtro [?()] — la parte potente

I filtri sono il punto in cui JSONPath si guadagna il pane. Un filtro [?()] mantiene solo gli elementi per cui un predicato è vero, e dentro il filtro @ si riferisce all’elemento corrente sotto esame.

Per selezionare i libri più economici di 10:

$.store.book[?(@.price < 10)].title
→ ["Sayings of the Century", "Moby Dick"]

Sui prezzi del bookstore (8.95, 12.99, 8.99, 22.99), due libri qualificano. Ecco come costruire i predicati di filtro passo passo:

  1. Confronta con un literal. Usa ==, !=, <, <=, >, >= — per esempio @.price > 10.
  2. Confronta una stringa. I literal di stringa vogliono gli apici singoli: @.author == 'Nigel Rees'.
  3. Verifica l’esistenza. Un semplice riferimento a un membro seleziona gli elementi che lo possiedono: [?(@.isbn)] mantiene solo i libri con un isbn.
  4. Combina condizioni. Unisci i predicati con && e ||: [?(@.price < 10 && @.author == 'Herman Melville')].

L’errore di filtro più frequente riguarda l’ambito di @. Dentro il predicato l’elemento corrente è @, non $. Scrivere $.price rimanda alla radice del documento, non al libro sotto esame:

$.store.book[?($.price < 10)]   → wrong scope, matches nothing useful
$.store.book[?(@.price < 10)]   → correct: each book's own price

RFC 9535 vs Classic nei filtri

I due dialetti divergono su spaziatura e quoting. Classic è permissivo: [?(@.price<10)] senza spazi viene analizzato senza problemi. RFC 9535 segue la sua grammatica alla lettera ed è più severo su come va scritto il filtro. Se un filtro che funzionava altrove fallisce, controlla la spaziatura e il motore. Mantieni i filtri puliti (operatori spaziati, stringhe tra apici singoli) e si valutano allo stesso modo a prescindere dalla libreria che li esegue.

Selettori di unione — selezionare più chiavi in una volta ([a,b])

Un selettore di unione elenca più nomi o indici dentro una sola parentesi e li raccoglie tutti:

$.store.book[0]['title','author']
→ ["Sayings of the Century", "Nigel Rees"]

Le unioni funzionano anche con gli indici e puoi mischiarle con altri selettori per una proiezione fissa:

$.store.book[0,2].title          → ["Sayings of the Century", "Moby Dick"]
$.store.book[*]['title','price']  → title and price of every book

Le unioni sono lo strumento giusto quando vuoi pochi campi specifici anziché un intero oggetto o una scansione con wildcard.

Funzioni RFC 9535: length, count, match, search, value

RFC 9535 definisce cinque estensioni di funzione standard. La regola che spiazza quasi tutti — e che le guide concorrenti sbagliano di continuo — è questa:

Queste funzioni si possono chiamare solo dentro un filtro [?...], mai come segmento di percorso a sé stante.

Scrivere $.store.book.length() non è valido in RFC 9535, e la grammatica standard lo rifiuta. Quella forma con chiamata su un segmento è un’estensione di jsonpath-plus, non fa parte della specifica. Per filtrare in base alla lunghezza, chiami la funzione dentro il predicato:

$.store.book[?length(@.title) > 15]
→ [
    { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
    { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
  ]

Entrambi i titoli selezionati sono più lunghi di 15 caratteri; “Moby Dick” (9) e “Sword of Honour” (15, non oltre 15) sono esclusi.

Ecco cosa fa ogni funzione dentro un filtro:

  • length() — lunghezza di una stringa, un array o un oggetto: [?length(@.title) > 15]
  • count() — numero di nodi in una nodelist: [?(count(@.authors) > 1)]
  • match() — test regex sull’intera stringa (pattern I-Regexp): [?match(@.author, 'J.*')]
  • search() — test regex su una sottostringa: [?search(@.title, 'the')]
  • value() — converte una nodelist a nodo singolo nel suo valore per il confronto

Tutte e cinque sono una funzionalità RFC 9535. Il dialetto Classic (Goessner) non le implementa, quindi se un’espressione basata su una funzione fallisce, verifica di chiamarla dentro un filtro e che il motore sia impostato su RFC 9535.

RFC 9535 vs Classic Goessner — perché la stessa espressione cambia

Quando un’espressione JSONPath restituisce risultati diversi in due strumenti, di solito la causa è il dialetto. Ecco come si confrontano i due:

AspettoClassic Goessner (2007)RFC 9535 (2024)
StandardizzazioneDe facto, mai formalizzatoPrima specifica IETF formale
Spaziatura/quoting nei filtriPermissivo ([?(@.price<10)] OK)Severo, segue la grammatica alla lettera
Confronto su membro mancanteDefinito dall’implementazioneBen definito, non solleva eccezioni
Funzioni standardNon fanno parte del dialettolength count match search value
Percorsi normalizzatiNessuna forma canonicaCanonica, forma a parentesi con apici singoli
Ordinamento delle unioniVaria da libreria a libreriaSpecificato

Consiglio pratico: se il tuo sistema a valle dichiara conformità a RFC 9535, scrivi e valida sul motore standard. Se stai mantenendo un’espressione copiata da jsonpath.com, jsonpath-plus o un servizio basato su Jayway, usa Classic così i risultati si riproducono. Il JSONPath Tester esegue entrambi i motori dietro un unico interruttore, così puoi incollare un’espressione una volta sola e vedere come ciascun dialetto la gestisce fianco a fianco. Quel confronto a doppio motore è il modo più rapido per diagnosticare una divergenza.

JSONPath vs XPath vs jq — quale usare

Questi tre vengono spesso confusi, quindi ecco la versione breve:

  • JSONPath è una query di percorso dichiarativa per JSON. Dà il meglio incorporato in configurazioni e asserzioni di test, dove vuoi nominare una posizione senza scrivere codice.
  • XPath è l’equivalente nel mondo XML. JSONPath ne ha preso in prestito parte della notazione (*, .., []), ed è per questo che l’analogia regge, ma i due linguaggi non sono intercambiabili e i set di funzioni differiscono.
  • jq è un processore JSON da riga di comando. Va ben oltre la selezione di percorsi (trasformazione, aggregazione, rimodellamento) e vive nella tua pipeline di shell.

La decisione di solito è netta. Per un’asserzione incorporata o un campo di configurazione di pipeline, scegli JSONPath. Per trasformazione e manipolazione di dati guidate dalla shell, scegli jq, e il jq Cheat Sheet copre quel flusso di lavoro in profondità. E quando la domanda è se un payload è conforme a una forma attesa anziché dove si trovi un campo, validalo con il JSON Schema Validator e la sua guida completa alla validazione.

7 errori comuni con JSONPath

  1. Dimenticare la radice $. store.book viene rifiutato dalla maggior parte dei motori; ogni espressione inizia con $.
  2. Slice off-by-one. [0:2] sono due elementi, non tre: il limite finale è esclusivo.
  3. Dialetto sbagliato. Eseguire un’espressione Classic sotto RFC 9535 (o viceversa) può dare un parse-error o corrispondere a nodi diversi. Cambia il motore per farlo combaciare.
  4. Funzione come segmento a sé stante. $.store.book.length() non è valido in RFC 9535; chiama length() dentro un filtro.
  5. Dimenticare @ in un filtro. [?($.price < 10)] punta alla radice; usa [?(@.price < 10)].
  6. Quoting sbagliato nelle parentesi. $[store] è un errore; metti la chiave tra apici: $['store'].
  7. Dare per scontato che .. si fermi al primo livello. La discesa ricorsiva corrisponde a ogni profondità, non solo ai figli diretti.

Testa JSONPath online, in privato

Il modo più rapido per imparare la sintassi JSONPath è eseguirla. Il JSONPath Tester valuta ogni espressione di questa guida in tempo reale: doppio motore RFC 9535 e Classic, viste dei risultati Values / Paths / Both, percorsi normalizzati per il debug ed esecuzione 100% nel browser: nessun upload, nessuna registrazione e nessun eval, quindi è sicuro per payload proprietari. Costruisci un percorso qui, conferma che selezioni esattamente i nodi che vuoi, poi incolla l’espressione validata direttamente nel tuo codice, nei test o nella pipeline.

Per il resto del flusso di lavoro JSON, trasforma una risposta di esempio in interfacce tipizzate con JSON to TypeScript, oppure confronta due documenti campo per campo con JSON Diff.

Domande frequenti

A cosa serve JSONPath?

JSONPath interroga JSON senza codice imperativo. Gli sviluppatori lo usano per estrarre campi dalle risposte API, fare asserzioni sui valori nei test di integrazione e indirizzare campi nelle configurazioni per Kubernetes, AWS Step Functions e Azure Logic Apps. Eccelle nell’estrarre dati da strutture grandi o irregolari, dove scrivere un attraversamento a mano sarebbe noioso.

Qual è la differenza tra RFC 9535 e JSONPath classico?

Classic è il dialetto de facto di Stefan Goessner del 2007, ampiamente implementato ma mai specificato formalmente, perciò le librerie hanno divergito. RFC 9535 è la specifica formale dell’IETF di febbraio 2024: definisce una grammatica precisa, percorsi normalizzati per i risultati e cinque funzioni standard. I due differiscono nei dettagli su filtri, unioni e confronto su membri mancanti.

Come funzionano le espressioni di filtro JSONPath?

Un filtro [?()] mantiene solo gli elementi il cui predicato è vero, e @ è l’elemento corrente. Per esempio, $.store.book[?(@.price < 10)] seleziona i libri con prezzo sotto 10. Puoi combinare condizioni con && e ||, verificare se un membro esiste e confrontare con literal di stringa o numero.

Posso usare length() come $.store.book.length()?

No. In RFC 9535, length() e le altre quattro funzioni si possono chiamare solo dentro un filtro, come $.store.book[?length(@.title) > 15]. La forma a segmento a sé stante $.store.book.length() è un’estensione di jsonpath-plus, non JSONPath standard, e la grammatica RFC 9535 la rifiuta.

JSONPath è la stessa cosa di XPath?

No, ma l’idea è simile. XPath interroga XML; JSONPath interroga JSON; entrambi individuano nodi con selettori di percorso. JSONPath ha preso deliberatamente in prestito parte della notazione di XPath (*, .. e []), il che rende l’analogia utile, ma sintassi, semantica e set di funzioni sono diversi e non intercambiabili.

Cosa fa la discesa ricorsiva (..) in JSONPath?

L’operatore .. cerca a ogni livello del documento, non solo i figli diretti. $..author raccoglie ogni membro author ovunque appaia, a qualsiasi profondità di annidamento. È il modo più rapido per estrarre un singolo campo da una struttura profondamente annidata o irregolare, ma può corrispondere a molti più nodi di quanti ti aspetti, perciò restringilo quando puoi.

Tag: jsonpath json query-language rfc-9535 filter-expression developer-tools

Articoli correlati

Vedi tutti gli articoli