Partiamo dal presupposto che tu abbia familiarità con la sintassi e i tipi di dati di JSON.
jq funziona facendo passare i dati JSON in ingresso attraverso una singola espressione (scritta come una pipeline di filtri), così da ottenere i dati trasformati desiderati.
Il linguaggio jq è implementato dal programma jq.
Questo programma offre diverse comode opzioni da riga di comando per controllare come viene consumato l'input e come viene presentato l'output.
Negli esempi che seguono incontrerai:
-n o --null-input
Normalmente al programma jq viene dato un file da leggere, oppure gli si inviano dei dati in input.
L'opzione --null-input ti permette di generare dati JSON senza alcun input.
-c o --compact-output
Per impostazione predefinita, jq formatta l'output in modo leggibile.
È estremamente utile per gli esseri umani vedere i dati ben formattati.
Tuttavia per le macchine non è necessario: l'opzione --compact-output rimuove gli spazi di formattazione per ridurre al minimo la dimensione del JSON risultante.
-f filename o --from-file filename
Legge il programma jq da filename invece di fornirlo sulla riga di comando.
Sia sed che awk usano l'opzione -f per lo stesso scopo.
Lo vedrai usato negli script di test degli esercizi di pratica.
Il resto di questa lezione si concentrerà sul linguaggio jq.
I filtri sono anche chiamati espressioni.
Un filtro riceve un input e produce un output.
Come quando lavori in una shell unix, puoi unire i filtri con una pipe | per collegare l'output di un filtro all'input di un altro.
.
Questo è il filtro più semplice. Si limita a passare il suo input al suo output.
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
Questa sarà una rapida introduzione all'uso degli array. Tratteremo questo argomento in modo più dettagliato più avanti.
Si accede agli elementi di un array con le parentesi quadre e l'indice parte da zero.
$ echo '[10, 20, 30]' | jq '.[1]'
20
Un filtro può costruire un array racchiudendo un'espressione tra [ e ]
con un elenco noto di elementi:
jq -n '[1, 2, 3]'
per raccogliere uno stream di elementi: per esempio,
range è una funzione che produce uno stream di numeri
$ jq -n 'range(10; 70; 15)'
10
25
40
55
Usare [] raccoglie i risultati dell'espressione in un array
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
La virgola non è solo sintassi che separa gli elementi di un array. La virgola è un operatore che unisce gli stream.
Per esempio, [1, 2, 3] è un filtro che usa il costruttore di array [] per raccogliere il risultato dell'unione delle tre espressioni 1, 2 e 3.
Hai notato i punti e virgola in range(10; 70; 15) qui sopra?
Poiché le virgole hanno uno scopo specifico nel linguaggio jq, le funzioni che accettano più argomenti usano i punti e virgola per separarli.
Una rapida introduzione agli oggetti.
Come in molti linguaggi di programmazione, usa i punti per accedere alle proprietà di un oggetto
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
Le parentesi si possono usare anche per gli oggetti, ma in quel caso servono le virgolette per le stringhe letterali. Questo è un modo per lavorare con chiavi che contengono spazi.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
Puoi costruire un oggetto con {} e coppie key: value.
Le virgolette non sono necessarie attorno alle chiavi che sono stringhe «semplici».
jq -n '{question: (6 * 9), answer: 42}'
produce
{
"question": 54,
"answer": 42
}
Per trattare la chiave come un'espressione, devi racchiuderla tra parentesi (anche il seguente esempio produce lo stesso risultato di sopra).
echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'
Per esempio, dato un file.json che contiene
{
"key1": "value1",
"key2": [5, 15, 25]
}
Calcoliamo la lunghezza dell'array key2:
$ jq '.key2 | length' file.json
3
Stiamo passando l'output dell'espressione .key2 come input a length attraverso una pipe; come ci si aspetta, produce il numero di elementi dell'array.
In questo esempio, i dati JSON in input vengono ignorati e non hanno alcun impatto sull'output:
$ echo '{"answer": 42}' | jq '6 * 9'
54
Un filtro può produrre più di un valore.
Per esempio, il filtro .[] produce ogni elemento di un array come valore separato:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
Passare un filtro di questo tipo a un altro eseguirà il secondo filtro per ogni valore:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
È come un'iterazione implicita.
Una volta capita questa tecnica, ti accorgerai che filtri jq molto potenti possono essere molto concisi.
Le parentesi si usano per raggruppare le sottoespressioni e imporre l'ordine delle operazioni, proprio come in altri linguaggi.
In jq, la loro necessità può sembrare alquanto sorprendente.
Per esempio, supponiamo di voler costruire un array con 2 elementi: la radice quadrata di 9; e e elevato alla potenza 1.
Le due singole espressioni sono 9 | sqrt e 1 | exp.
Ci aspettiamo che l'output sia l'array [3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
Perché non abbiamo ottenuto quello che ci aspettavamo? jq interpreta così:
[ ((9|sqrt), 1) | exp ]
jq costruisce uno stream di due elementi (3 e 1), che vengono dati entrambi a exp.
Dobbiamo assicurarci che exp riceva un solo numero come input.
In altre parole, dobbiamo imporre che la pipe venga valutata prima della virgola.
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
Senza addentrarci troppo (le funzioni saranno l'argomento di un altro esercizio), ecco alcune utili funzioni integrate:
length
Dato un array in input, produce il numero di elementi dell'array.
$ jq -n '[10, 20, 30, 40] | length'
4
+
Questo operatore fa cose diverse a seconda del tipo dei suoi operandi: somma numeri, concatena stringhe, accoda array, unisce oggetti.
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add è una funzione che prende un array e restituisce un elemento con tutti gli elementi sommati insieme secondo le regole di +.
[1, 2, 3] | add produce 6.
map
Dato un array in input e un filtro come argomento, produce un array in cui il filtro è applicato a ogni elemento
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
select
Dato un qualche input e un filtro come argomento:
null, proprio nessun output)Per esempio, dati alcuni numeri, seleziona quelli divisibili per 3
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
Ricorda che range produce uno stream di numeri.
select verrà chiamato una volta per ogni numero.
Vengono prodotti solo i numeri che «superano» l'espressione.
Spesso ti serve selezionare elementi di un array. Ci sono un paio di modi per farlo.
Con l'input ["Anne", "Bob", "Cathy", "Dave"], seleziona i nomi che hanno lunghezza 4.
usa map e select insieme
map(select(length == 4))
esplodi l'array in elementi, applica select su quello stream e raccogli i risultati
[ .[] | select(length == 4) ]
I commenti iniziano con il carattere # e proseguono fino alla fine della riga.
Ti è venuta improvvisamente voglia di pancake. Da persona moderna e incline alla tecnica, scrivi una lista della spesa per gli ingredienti dei pancake in formato JSON. La struttura della lista è:
{
"name": "name of shopping list",
"ingredients": [ ...list of ingredients... ],
"optional ingredients": [ ...list of ingredients... ]
}
Un ingrediente è rappresentato da un oggetto JSON, come questo:
{
"item": "flour",
"amount": {
"quantity": 2,
"unit": "cup"
}
}
Un ingrediente ha una proprietà opzionale chiamata "substitute" che contiene una stringa con un altro elemento che può essere usato al suo posto.
Vediamo come possiamo esaminare la lista della spesa con jq
Scrivi un'espressione che produca come output l'elemento "name" della lista della spesa.
Conta solo gli ingredienti «obbligatori». Non includere gli ingredienti opzionali.
Scrivi un'espressione che produca come output la quantità di zucchero. Serve solo la parte numerica.
Alcuni ingredienti possono essere sostituiti (se non hai l'ingrediente X puoi usare Y). Produci come output un oggetto JSON che associa l'ingrediente consigliato al suo sostituto. Includi gli ingredienti opzionali nella mappatura.
Iscriviti a Exercism per imparare e padroneggiare jq con 12 concetti75 esercizi e il mentoring di persone reali, tutto gratis.