JSON Toolkit

JMESPath per principianti: query di proiezione che battono JSONPath

In sintesi: JMESPath rimodella lotti di oggetti più direttamente di JSONPath. Questa guida per principianti copre sintassi, proiezioni, pipe, funzioni integrate e uso con AWS CLI.

Principiante~9 min di letturaPubblicato il 2026-10-31Aggiornato il 2026-10-31
Copertina di JMESPath per principianti

In sintesi: usa JSON Pointer per localizzare per posizione, JSONPath per filtrare o ricorrere, e JMESPath quando devi rimodellare il risultato o proiettarlo sulla riga di comando; un singolo campo noto non necessita di nessuno dei tre.

Se il tuo obiettivo è rimodellare un lotto di oggetti in una forma diversa, JMESPath è spesso più diretto di JSONPath. Non cerca nodi; costruisce un risultato.

1. Cos'è JMESPath

JMESPath è un linguaggio di interrogazione progettato specificamente per JSON. Fu creato in origine per filtrare l'output di AWS CLI e in seguito si è diffuso in molti linguaggi attraverso implementazioni come jmespath.js, jmespath.py e go-jmespath. Il suo obiettivo differisce da JSONPath:

  • JSONPath si concentra sul selezionare un insieme di nodi per percorso e condizione, e restituisce un insieme di nodi;
  • JMESPath si concentra sul trasformare il JSON di input e restituisce un valore di qualsiasi forma.

Questa differenza rende JMESPath particolarmente adatto alle proiezioni di liste, alla ridenominazione dei campi e alla rifinitura dei risultati, come ridurre venti campi da una risposta API ai tre campi di cui il frontend ha davvero bisogno. Se non hai ancora letto le basi di JSONPath, inizia da Il linguaggio di interrogazione JSONPath in pratica.

2. Sintassi di base: dagli identificatori agli indici

JMESPath usa meno simboli di JSONPath e si legge più come un'espressione di estrazione:

Sintassi Significato Esempio
name Accede a un membro dell'oggetto people
a.b Accede a membri annidati store.bicycle.color
[0] Accede a un indice dell'array people[0]
[-1] Accede all'ultimo elemento people[-1]
[0:2] Slice, inizio incluso e fine esclusa people[0:2]
[*] Proiezione di lista people[*].name
[] Proiezione di appiattimento people[].tags[]
[?condition] Proiezione di filtro people[?age > 18]
{key: expr} Hash multi-selezione {name: name, age: age}
[expr, expr] Lista multi-selezione [name, age]
| Passa il risultato sinistro al lato destro people[*].age | [0]

Un esempio minimo:

{
  "people": [
    { "name": "Alice", "age": 31, "city": "Shanghai" },
    { "name": "Bob", "age": 17, "city": "Beijing" }
  ]
}
people[0].name      → "Alice"
people[-1].city     → "Beijing"
people[0:2].name    → ["Alice", "Bob"]

Nota perché people[0:2].name restituisce un array: uno slice restituisce una lista, e il successivo .name viene automaticamente proiettato su ogni elemento di quella lista. Questa è la più grande differenza tra JMESPath e l'accesso ordinario agli oggetti.

3. Proiezioni: trasformare una lista in un'altra

Una proiezione è il concetto centrale di JMESPath. Funziona in due passi: valuta ogni elemento di una lista, poi raccoglie i risultati in una nuova lista. Gli elementi che valutano a null vengono eliminati automaticamente.

people[*].name                   // ["Alice", "Bob"]
people[*].{name: name, age: age} // a list of objects
people[?age > 18].name           // ["Alice"]

La seconda riga è un'espressione di proiezione tipica: people[*].{name: name, age: age}. Ricostruisce ogni elemento come { "name": ..., "age": ... } e scarta il campo city in più. Rispetto a JSONPath, che ti dà nodi che poi mappi nel codice, questo risparmia un ciclo.

Vale la pena distinguere due tipi di proiezioni:

  • [*] preserva la struttura della lista originale e salta i valori null;
  • [] è una proiezione di appiattimento che prima collassa un livello di liste annidate, il che si adatta a espressioni come people[].tags[] che necessitano di "tutti i tag di tutte le persone".

4. Pipe e multi-selezione

La pipe | significa "ferma la proiezione corrente e tratta il risultato come un nuovo input". Il suo uso più comune è prendere un indice dopo una proiezione:

people[*].name | [0]           → "Alice"
people[*].age | sort(@) | [-1] → the maximum age

Senza la pipe, people[*].name[0] viene interpretato come prendere il primo carattere di ogni nome, che non è ciò che vuoi. La pipe è il simbolo più facilmente trascurato e più utile di JMESPath.

Esistono due forme di multi-selezione:

// Multi-select hash: the result is an object
people[0].{name: name, age: age}

// Multi-select list: the result is an array
people[0].[name, age]

Gli hash multi-selezione sono adatti a costruire risultati con nome, mentre le liste multi-selezione sono adatte al consumo posizionale. Entrambi possono essere annidati dentro le proiezioni, ma nota: in un hash multi-selezione, una chiave il cui valore è null viene comunque mantenuta, mentre una proiezione elimina l'intero elemento null.

5. Funzioni integrate

Lo standard JMESPath definisce un insieme di funzioni. Quella comuni includono:

Funzione Scopo Esempio
length() Ottiene la lunghezza length(people)
sort() Ordina i valori sort(people[*].age)
sort_by() Ordina per un campo sort_by(people, &age)
max() / min() Massimo / minimo max(people[*].age)
sum() Somma i valori sum(people[*].age)
keys() Ottiene le chiavi dell'oggetto keys(people[0])
contains() Verifica il contenimento contains(name, "Ali")
to_string() Converte in stringa to_string(age)

Il secondo argomento di sort_by è un'espressione &, che significa "prendi questo campo da ogni elemento":

sort_by(people, &age)[*].name

Queste funzioni permettono a JMESPath di fare semplici aggregazioni, mentre JSONPath è progettato solo per la selezione; sommare e raggruppare di solito richiede jq o codice.

6. Un confronto fianco a fianco con JSONPath

Dimensione JSONPath JMESPath
Obiettivo principale Selezionare nodi Trasformare i dati
Simbolo di radice $ Nessuno; scrivi direttamente i nomi dei campi
Nodo corrente @ @ (solo negli argomenti delle funzioni)
Filtro [?(@.age > 18)] [?age > 18]
Wildcard [*] [*] o []
Costruire nuovi oggetti Non supportato {name: name}
Pipe Non supportate |
Standard RFC 9535 JMESPath Specification

Una guida decisionale in una frase:

  • Usa JSONPath quando devi solo trovare nodi che corrispondono a una condizione;
  • Usa JMESPath quando devi rimodellare il risultato in ciò che il frontend si aspetta;
  • Non usare nessuno dei due quando leggi un campo noto; accedi direttamente alla proprietà.

Se il risultato della query deve essere riscritto nel documento, nessuno dei due linguaggi è appropriato. Usa invece JSON Pointer e le patch; vedi JSON Pointer, Patch e Merge Patch.

7. JMESPath in AWS CLI

Il parametro --query di AWS CLI è JMESPath, che è il suo caso d'uso più diffuso:

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
  --output table

Questo comando fa tre cose: appiattisce due liste annidate con [], mantiene solo tre campi con un hash multi-selezione, e renderizza il risultato come tabella con --output table. Con JSONPath avresti bisogno di più livelli e non otterresti l'effetto "rinomina più rifila".

Altre forme comuni:

# Only running instances
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'

# The value of the Name tag
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'

Nota che i backtick racchiudono i letterali, non le stringhe. In AWS CLI, le stringhe usano le virgolette singole e i letterali JSON usano i backtick. Questa differenza è uno degli errori più comuni dei principianti.

8. Errori comuni

  • Confondere [*] e []: il primo preserva l'annidamento, il secondo appiattisce un livello, e le strutture risultanti sono completamente diverse;
  • Dimenticare la pipe: people[*].name[0] prende il primo carattere di ogni nome, non il primo nome;
  • Scrivere @ dentro un filtro: i filtri JMESPath usano direttamente i nomi dei campi, quindi [?@.age > 18] è sbagliato;
  • Trattare null come una stringa vuota: le proiezioni eliminano null, ma gli hash multi-selezione mantengono la chiave;
  • Usare virgolette doppie attorno ai letterali in AWS CLI: la shell le espande prima e riscrive l'espressione.

Consiglio di debug: valida un'espressione sull'input più piccolo possibile prima di applicarla ai dati reali, e aggiungi --output json in AWS CLI per vedere i risultati grezzi. Per l'editing e la formattazione quotidiana di JSON, abbinare a Editor JSON e strumenti da riga di comando rende molto più rapida la localizzazione dei percorsi dei campi.

FAQ

JMESPath e JSONPath possono essere usati insieme?

Sì, ma di solito non è necessario. Un approccio comune è rifilare un piccolo oggetto con JMESPath, poi usare l'accesso ordinario alle proprietà nel codice. Se devi davvero localizzare per percorso e poi modificare, JSONPath legge e JSON Pointer scrive, che è una divisione del lavoro più chiara.

Perché people[*].name restituisce qualcosa di diverso da una singola stringa?

Perché [*] è una proiezione, e il risultato è sempre una lista, anche quando c'è una sola persona. Per ottenere un singolo valore, aggiungi una pipe e un indice: people[*].name | [0].

Cosa succede a null dentro una proiezione?

Una proiezione di lista salta automaticamente gli elementi che valutano a null, quindi il risultato può essere più corto della lista originale. Un hash multi-selezione mantiene la chiave e preserva il valore null, il che conta quando si costruiscono risposte API.

JMESPath può fare aggregazione e raggruppamento?

Può fare aggregazione di base con sum(), max() e sort_by(). Raggruppamento, join e window function sono fuori dallo standard. Lascia l'analisi complessa a jq, SQL o codice.

Come decido tra JMESPath e JSONPath?

Usa JSONPath quando devi selezionare nodi per percorso e condizione e ricevere un insieme di nodi; usa JMESPath quando devi rimodellare il risultato in ciò che il frontend si aspetta, con ridenominazione e rifinitura dei campi. Quando leggi un campo noto, non usare nessuno dei due; accedi direttamente alla proprietà.

Devo usare una pipe per prendere il primo valore proiettato?

Sì. people[*].name è una proiezione, quindi il risultato è sempre una lista; senza pipe, people[*].name[0] prende il primo carattere di ogni nome. Scrivi people[*].name | [0] per ottenere il primo valore.