Vamos assumir que você já conhece a sintaxe e os tipos de dados do JSON.
O jq funciona passando os dados JSON de entrada por uma única expressão (escrita como um pipeline de filtros) para obter os dados transformados desejados.
A linguagem jq é implementada pelo programa jq.
Esse programa oferece várias opções de linha de comando úteis para controlar como a entrada é consumida e como a saída é apresentada.
Nos exemplos abaixo você encontrará:
-n ou --null-input
Normalmente, o programa jq recebe um arquivo para ler, ou você envia dados para a entrada dele.
A opção --null-input permite gerar dados JSON sem nenhuma entrada.
-c ou --compact-output
O jq formata a saída de forma legível por padrão.
É extremamente útil para humanos visualizar os dados quando estão bem formatados.
No entanto, isso não é necessário para máquinas: a opção --compact-output remove os espaços em branco da formatação para minimizar o tamanho do JSON resultante.
-f filename ou --from-file filename
Lê o programa jq de filename em vez de fornecê-lo na linha de comando.
Tanto sed quanto awk usam a opção -f para o mesmo propósito.
Você verá isso sendo usado nos scripts de teste para os exercícios de prática.
O restante desta lição vai focar na linguagem jq.
Filtros também são conhecidos como Expressões.
Um filtro recebe uma entrada e produz uma saída.
Assim como você trabalha em um shell Unix, você pode unir filtros com um pipe | para conectar a saída de um filtro à entrada de outro.
.
Este é o filtro mais simples. Ele simplesmente passa sua entrada para sua saída.
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
Esta será uma introdução rápida ao trabalho com arrays. Vamos abordar este tópico em mais detalhes depois.
Elementos de array são acessados com colchetes, e são indexados a partir de zero.
$ echo '[10, 20, 30]' | jq '.[1]'
20
Um filtro pode construir um array envolvendo uma expressão em [ e ]
com uma lista conhecida de elementos:
jq -n '[1, 2, 3]'
para coletar um stream de elementos: por exemplo,
range é uma função que gera um stream de números
$ jq -n 'range(10; 70; 15)'
10
25
40
55
Usar [] coleta os resultados da expressão em um array
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
A vírgula não é apenas sintaxe que separa elementos de array. Vírgula é um operador que une streams.
Por exemplo, [1, 2, 3] é um filtro que usa o construtor de array [] para coletar o resultado da união das três expressões 1, 2 e 3.
Você notou os pontos e vírgulas em range(10; 70; 15) acima?
Como as vírgulas têm um propósito específico na linguagem jq, funções que recebem múltiplos argumentos usam pontos e vírgulas para separar os argumentos.
Uma introdução rápida a objetos.
Semelhante a muitas linguagens de programação, use pontos para acessar propriedades de objeto
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
Colchetes também podem ser usados para objetos, mas então aspas são necessárias para strings literais. Este é um método para trabalhar com chaves contendo espaços.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
Você pode construir um objeto com {} e pares key: value.
Aspas não são necessárias ao redor de chaves que são strings "simples".
jq -n '{question: (6 * 9), answer: 42}'
produz
{
"question": 54,
"answer": 42
}
Para tratar a chave como uma expressão, você deve envolvê-la em parênteses (o seguinte também produz o mesmo que acima).
echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'
Por exemplo, dado file.json contendo
{
"key1": "value1",
"key2": [5, 15, 25]
}
Vamos calcular o comprimento do array key2:
$ jq '.key2 | length' file.json
3
Estamos passando a saída da expressão .key2 como entrada para length, que sem surpresa produz o número de elementos no array.
Neste exemplo, os dados JSON de entrada são ignorados e não têm impacto na saída:
$ echo '{"answer": 42}' | jq '6 * 9'
54
Um filtro pode gerar mais de um valor.
Por exemplo, o filtro .[] gera cada elemento de um array como um valor separado:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
Passar esse filtro para outro executará o segundo filtro para cada valor:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
Isso é como iteração implícita.
Depois que você entender esta técnica, vai perceber que filtros jq muito poderosos podem ser bem concisos.
Parênteses são usados para agrupar subexpressões e impor a ordem das operações, assim como em outras linguagens.
No jq, a necessidade deles pode parecer um pouco surpreendente.
Por exemplo, digamos que queremos construir um array com 2 elementos: a raiz quadrada de 9; e e elevado à potência 1.
As duas expressões individuais são 9 | sqrt e 1 | exp.
Esperamos que a saída seja o array [3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
Por que não obtivemos o que esperávamos? O jq interpreta isso assim:
[ ((9|sqrt), 1) | exp ]
O jq constrói um stream de dois elementos (3 e 1) que são cada um passados para exp.
Precisamos garantir que exp receba apenas um número como entrada.
Em outras palavras, precisamos impor que o pipe seja avaliado antes da vírgula.
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
Sem entrar em grandes detalhes (funções serão um tópico para outro exercício), aqui estão algumas funções internas úteis:
length
Dado um array como entrada, produz o número de elementos no array.
$ jq -n '[10, 20, 30, 40] | length'
4
+
Este operador faz coisas diferentes dependendo do tipo de seus operandos: soma números, concatena strings, anexa arrays, mescla objetos.
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add é uma função que recebe um array e retorna um item com todos os elementos somados usando as regras do +.
[1, 2, 3] | add produz 6.
map
Dado um array como entrada e um filtro como argumento, produz um array onde o filtro é aplicado a cada elemento
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
select
Dada alguma entrada e um filtro como argumento:
null, realmente nenhuma saída)Por exemplo, dados alguns números, selecione os divisíveis por 3
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
Lembre-se de que range produz um stream de números.
select será invocado uma vez para cada número.
Apenas os números que "passam" na expressão são produzidos.
Frequentemente você precisa selecionar elementos de um array. Há algumas maneiras de fazer isso.
Com a entrada ["Anne", "Bob", "Cathy", "Dave"], selecione os nomes com comprimento 4.
use map e select juntos
map(select(length == 4))
exploda o array em elementos, aplique select nesse stream, e colete os resultados
[ .[] | select(length == 4) ]
Comentários começam com um caractere # e continuam até o final da linha.
Você está com uma vontade súbita de comer panquecas. Como uma pessoa moderna e ligada em tecnologia, você escreve uma lista de compras dos ingredientes da panqueca em formato JSON. A estrutura da lista é:
{
"name": "name of shopping list",
"ingredients": [ ...list of ingredients... ],
"optional ingredients": [ ...list of ingredients... ]
}
Um ingrediente é representado por um objeto JSON, assim:
{
"item": "flour",
"amount": {
"quantity": 2,
"unit": "cup"
}
}
Um ingrediente tem uma propriedade opcional chamada "substitute" que guarda uma string com outro item que pode ser usado no lugar.
Vamos ver como podemos examinar a lista de compras com jq.
Escreva uma expressão que imprime o elemento "name" da lista de compras.
Conte apenas os ingredientes "obrigatórios". Não inclua os ingredientes opcionais.
Escreva uma expressão que imprime a quantidade de açúcar. Só a parte numérica interessa.
Alguns dos ingredientes podem ser substituídos (se você não tem o ingrediente X, pode usar o Y). Imprima um objeto JSON que mapeia o ingrediente recomendado à sua substituição. Inclua os ingredientes opcionais no mapeamento.
Crie sua conta no Exercism para aprender e dominar jq com 12 conceitos75 exercícios e mentoria humana de verdade, tudo de graça.