Tester JSONPath
Scrivi un’espressione JSONPath e vedi le corrispondenze in tempo reale. Sintassi RFC 9535.
Nulla di ciò che incolli lascia il tuo browser. L’allowlist connect-src ne fa una garanzia del browser anziché una promessa. Verificalo tu stesso
Scrivi un’espressione JSONPath e vedi ogni corrispondenza sul tuo documento, con il percorso normalizzato di ciascuna. Modifica ed esegui di nuovo finché non seleziona ciò che intendevi.
La sintassi qui segue la RFC 9535, lo standard proposto dall’IETF pubblicato a febbraio 2024. Conta più di quanto sembri, perché per i diciassette anni precedenti non esisteva alcuna specifica.
Perché il dialetto va dichiarato
JSONPath è nato nel 2007 come post sul blog di Stefan Goessner. È stato implementato ovunque e mai specificato, e le implementazioni sono divergute su quasi tutto ciò che conta: se $.. includa la radice, che cosa significhi un indice negativo, se $[0,1] faccia unione, come si comporti un filtro quando la chiave manca, e che cosa succeda con un passo di slice pari a zero. Un progetto di confronto ha catalogato centinaia di questi disaccordi.
La RFC 9535 ha messo ordine. Un tester che non dichiara quale dialetto implementa ti dà la risposta senza darti la domanda, quindi questo lo dichiara: RFC 9535, con le esclusioni elencate sotto.
Sintassi supportata qui
- $
- La radice del documento. Ogni espressione parte da qui.
- .name e ['name']
- Un membro con nome. Usa la forma tra parentesi quadre per nomi con spazi o punteggiatura.
- .* e [*]
- Ogni membro di un oggetto, oppure ogni elemento di un array.
- ..
- Un segmento discendente: cerca a questo livello e a tutti quelli sottostanti.
- [0] e [-1]
- Un indice di array. Il negativo conta dalla fine.
- [1:5], [::2], [::-1]
- Uno slice, con la semantica della RFC 9535. Un passo negativo scorre all’indietro.
- [0, 2, 'name']
- Più selettori in un unico segmento, che producono l’unione dei loro risultati.
- [?<espressione>]
- Un filtro. Al suo interno, @ è l’elemento corrente e $ è la radice del documento. Confronto con == != < <= > >=, combinabile con && || e !.
- length() count() match() search() value()
- Le estensioni di funzione definite dalla RFC 9535. match() ancora l’intera stringa; search() no.
Deliberatamente non supportato
Le espressioni di script nella forma [(...)] non sono mai state specificate e la RFC 9535 le ha rimosse. L’operatore genitore ^ è un’estensione aggiunta da alcune implementazioni e assente dalla RFC. E @.length come pseudo-proprietà è la scrittura pre-RFC di quello che oggi è length(@); se ne incolli una, il tester te lo dice invece di non restituire nulla in silenzio.
JSONPath, JMESPath, jq e JSON Pointer
Quattro modi di indirizzare parti di un documento JSON, per quattro lavori diversi.
- JSONPath
- Seleziona un insieme di nodi. Il migliore quando vuoi tutto ciò che corrisponde a uno schema, a qualsiasi profondità. Ora standardizzato come RFC 9535.
- JMESPath
- Oltre a selezionare, trasforma: proiezioni, multiselect hash ed espressioni con pipe permettono di rimodellare l’output. Usato dalla CLI di AWS. Con una vera specifica fin dall’inizio.
- jq
- Un linguaggio completo con una sintassi di query attaccata. Da usare quando l’operazione somiglia più a programmare che a selezionare.
- JSON Pointer, RFC 6901
- Indirizza esattamente una posizione, senza caratteri jolly e senza filtri. Deliberatamente banale, ed è per questo che lo usano sia JSON Patch sia JSON Schema. Due escape: ~0 per una tilde e ~1 per una barra.
How to do this in code
Eseguire la stessa query nel codice.
py Python
# jsonpath-ng is the most complete Python implementation
from jsonpath_ng.ext import parse
expr = parse('$.store.book[?(@.price < 10)].title')
titles = [m.value for m in expr.find(data)]
# JMESPath, if you prefer a specified language with projections
import jmespath
titles = jmespath.search('store.book[?price < `10`].title', data) js JavaScript
import { JSONPath } from 'jsonpath-plus';
const titles = JSONPath({
path: '$.store.book[?(@.price < 10)].title',
json: data,
});
// Get the normalised paths rather than the values
const paths = JSONPath({ path: '$..author', json: data, resultType: 'path' }); sh jq
jq non ha un operatore discendente-con-filtro, quindi le due metà si scrivono separatamente.
# The jq equivalent of a filtered descendant search
jq '.store.book[] | select(.price < 10) | .title' data.json
# Every value at any depth under a key
jq '.. | .author? // empty' data.json java Java
Jayway JsonPath è precedente alla RFC 9535 e se ne discosta in alcuni punti, in particolare sui filtri applicati a chiavi mancanti.
import com.jayway.jsonpath.JsonPath;
List<String> titles = JsonPath.read(json, "$.store.book[?(@.price < 10)].title"); Domande frequenti
- Perché la mia espressione non restituisce nulla?
- Di solito per una di tre ragioni: un nome che richiede le parentesi quadre con virgolette perché contiene uno spazio o un trattino, un filtro che confronta con una stringa senza virgolette (scrivi @.type == 'book', non @.type == book), oppure un percorso che presuppone un array dove il documento ha un oggetto. Il tester segnala un errore di analisi con la posizione quando è l’espressione a essere malformata, e un risultato vuoto solo quando l’espressione è valida ma non corrisponde a nulla.
- Che cos’è un percorso normalizzato?
- La RFC 9535 definisce una scrittura canonica per la posizione di una corrispondenza: nomi tra parentesi quadre e virgolette e indici numerici, come in $['store']['book'][0]['title']. Ogni corrispondenza qui ne mostra uno, il che rende i risultati confrontabili tra implementazioni.
- $..* è la stessa cosa di $..?
- No, ed è una delle divergenze che la RFC ha risolto. $..* seleziona ogni nodo discendente escludendo la radice; un $.. da solo non è nemmeno un’espressione completa.