JSON é um dos formatos mais populares para transferência de dados baseados em texto na web. Está em toda parte, e você com certeza vai encontrar. Mostraremos como lidar com isso na linha de comando do Linux usando o jq
comando.
Índice
JSON e jq
JSON significa JavaScript Object Notation . É um esquema que permite que os dados sejam codificados em arquivos de texto simples, de forma autodescritiva. Não há comentários em um arquivo JSON – o conteúdo deve ser autoexplicativo. Cada valor de dados tem uma string de texto chamada “nome” ou “chave”. Isso informa qual é o valor dos dados. Juntos, eles são conhecidos como pares nome: valor ou pares chave: valor. Dois pontos ( :
) separam uma chave de seu valor.
Um “objeto” é uma coleção de pares chave: valor. Em um arquivo JSON, um objeto começa com uma chave aberta ( {
) e termina com uma chave de fechamento ( }
). JSON também oferece suporte a “matrizes”, que são listas ordenadas de valores. Uma matriz começa com um colchete de abertura ( [
) e termina com um de fechamento ( ]
).
A partir dessas definições simples, é claro, pode surgir uma complexidade arbitrária. Por exemplo, objetos podem ser aninhados em objetos. Os objetos podem conter arrays e os arrays também podem conter objetos. Todos os quais podem ter níveis abertos de aninhamento.
Na prática, porém, se o layout dos dados JSON for complicado, o design do layout dos dados provavelmente deve ser repensado. Claro, se você não está gerando os dados JSON, apenas tentando usá-los, você não tem voz no layout. Nesses casos, infelizmente, você apenas tem que lidar com isso.
A maioria das linguagens de programação tem bibliotecas ou módulos que permitem analisar dados JSON. Infelizmente, o shell Bash não tem essa funcionalidade .
A necessidade sendo a mãe da invenção, porém, jq
nasceu a utilidade! Com jq
, podemos facilmente analisar JSON no shell Bash. E não importa se você tem que trabalhar com um JSON elegante e bem projetado ou com o material de que são feitos os pesadelos.
Como instalar o jq
Tivemos que instalar jq
em todas as distribuições Linux que usamos para pesquisar este artigo.
Para instalar jq
no Ubuntu, digite este comando:
sudo apt-get install jq
Para instalar jq
no Fedora, digite este comando:
sudo dnf install jq
Para instalar jq
no Manjaro, digite este comando:
sudo pacman -Sy jq
Como tornar JSON legível
JSON não se preocupa com o espaço em branco e o layout não o afeta. Desde que siga as regras da gramática JSON , os sistemas que processam JSON podem lê-lo e entendê-lo. Por causa disso, JSON é frequentemente transmitido como uma string simples e longa, sem nenhuma consideração de layout. Isso economiza um pouco de espaço porque tabulações, espaços e caracteres de nova linha não precisam ser incluídos no JSON. Claro, a desvantagem de tudo isso é quando um humano tenta ler.
Vamos retirar um objeto JSON curto do site da NASA que nos diz a posição da Estação Espacial Internacional . Usaremos curl
, que pode baixar arquivos para recuperar o objeto JSON para nós.
Não nos importamos com nenhuma das mensagens de status curl
geralmente geradas, então digitaremos o seguinte, usando a -s
opção (silenciosa):
curl -s http://api.open-notify.org/iss-now.json
Agora, com um pouco de esforço, você pode ler isso. Você tem que escolher os valores dos dados, mas não é fácil ou conveniente. Vamos repetir isso, mas desta vez vamos canalizar jq
.
jq
usa filtros para analisar JSON, e o mais simples desses filtros é um ponto ( .
), que significa “imprimir o objeto inteiro”. Por padrão, jq
imprime bem a saída.
Juntamos tudo e digitamos o seguinte:
curl -s http://api.open-notify.org/iss-now.json | jq.
Isso é muito melhor! Agora, podemos ver exatamente o que está acontecendo.
Todo o objeto é envolvido por colchetes. Ele contém duas chaves: pares de nomes: message
e timestamp
. Ele também contém um objeto chamado iss_position
, que contém dois pares de chave: valor: longitude
e latitude
.
Vamos tentar mais uma vez. Desta vez, digitaremos o seguinte e redirecionaremos a saída para um arquivo chamado “iss.json”:
curl -s http://api.open-notify.org/iss-now.json | jq. > iss.json
cat iss.json
Isso nos dá uma cópia bem definida do objeto JSON em nosso disco rígido.
Acessando valores de dados
Como vimos acima, jq
pode extrair valores de dados canalizados de JSON. Ele também pode funcionar com JSON armazenado em um arquivo. Vamos trabalhar com arquivos locais para que a linha de comando não fique entulhada de curl
comandos. Isso deve tornar um pouco mais fácil de seguir.
A maneira mais simples de extrair dados de um arquivo JSON é fornecer um nome de chave para obter seu valor de dados. Digite um ponto e o nome da chave sem espaço entre eles. Isso cria um filtro a partir do nome da chave. Também precisamos informar jq
qual arquivo JSON usar.
Nós digitamos o seguinte para recuperar o message
valor:
jq .message iss.json
jq
imprime o texto do message
valor na janela do terminal.
Se você tiver um nome de chave que inclua espaços ou pontuação, deverá colocar o filtro entre aspas. Geralmente, toma-se cuidado ao usar caracteres, números e sublinhados apenas para que os nomes de chave JSON não sejam problemáticos.
Primeiro, digitamos o seguinte para recuperar o timestamp
valor:
jq .timestamp iss.json
O valor do carimbo de data / hora é recuperado e impresso na janela do terminal.
Mas como podemos acessar os valores dentro do iss_position
objeto? Podemos usar a notação de ponto JSON. Incluiremos o iss_position
nome do objeto no “caminho” para o valor da chave. Para fazer isso, o nome do objeto dentro da chave precederá o nome da própria chave.
Nós latitude
digitamos o seguinte, incluindo o nome da chave (observe que não há espaços entre “.iss_position” e “.latitude”):
jq .iss_position.latitude iss.json
Para extrair vários valores, você deve fazer o seguinte:
- Liste os nomes das chaves na linha de comando.
- Separe-os com vírgulas (
,
). - Coloque-os entre aspas (
"
) ou apóstrofos ('
).
Com isso em mente, digitamos o seguinte:
jq ".iss_position.latitude, .timestamp" iss.json
Os dois valores são impressos na janela do terminal.
Trabalho com matrizes
Vamos pegar um objeto JSON diferente da NASA.
Desta vez, usaremos uma lista dos astronautas que estão no espaço agora :
curl -s http://api.open-notify.org/astros.json
Ok, funcionou, então vamos fazer de novo.
Vamos digitar o seguinte para jq
encaminhá-lo e redirecioná-lo para um arquivo chamado “astro.json”:
curl -s http://api.open-notify.org/astros.json | jq. > astro.json
Agora vamos digitar o seguinte para verificar nosso arquivo:
menos astro.json
Conforme mostrado abaixo, agora vemos a lista de astronautas no espaço, bem como suas naves espaciais.
Este objeto JSON contém uma matriz chamada people
. Sabemos que é uma matriz por causa do colchete de abertura ( [
) (destacado na imagem acima). É uma matriz de objetos em que cada um contém duas chaves: pares de valores: name
e craft
.
Como fizemos anteriormente, podemos usar a notação de ponto JSON para acessar os valores. Devemos também incluir os colchetes ( []
) no nome do array.
Com tudo isso em mente, digitamos o seguinte:
jq ".people []. name" astro.json
Desta vez, todos os valores de nome são impressos na janela do terminal. O que pedimos jq
para fazer foi imprimir o valor do nome para cada objeto no array. Muito legal, hein?
Podemos recuperar o nome de um único objeto se colocarmos sua posição no array entre colchetes ( []
) na linha de comando. A matriz usa indexação de deslocamento zero , o que significa que o objeto na primeira posição da matriz é zero.
Para acessar o último objeto na matriz, você pode usar -1; para obter o penúltimo objeto na matriz, você pode usar -2 e assim por diante.
Às vezes, o objeto JSON fornece o número de elementos no array, que é o caso deste. Junto com a matriz, ele contém um par chave: nome chamado number
com um valor de seis.
O seguinte número de objetos está nesta matriz:
jq ".people [1] .name" astro.json
jq ".people [3] .name" astro.json
jq ".people [-1] .name" astro.json
jq ".people [-2] .name" astro.json
Você também pode fornecer um objeto inicial e final dentro da matriz. Isso é chamado de “fatiar” e pode ser um pouco confuso. Lembre-se de que a matriz usa um deslocamento zero.
Para recuperar os objetos da posição de índice dois, até (mas não incluindo) o objeto na posição de índice quatro, digitamos o seguinte comando:
jq ".people [2: 4]" astro.json
Isso imprime os objetos no índice de array dois (o terceiro objeto no array) e três (o quarto objeto no array). Ele interrompe o processamento no índice de matriz quatro, que é o quinto objeto da matriz.
A maneira de entender melhor isso é experimentar na linha de comando. Você logo verá como funciona.
Como usar tubos com filtros
Você pode canalizar a saída de um filtro para outro e não precisa aprender um novo símbolo. O mesmo que a linha de comando do Linux, jq
usa a barra vertical ( |
) para representar um tubo.
Diremos jq
para canalizar o people
array para o .name
filtro, que deve listar os nomes dos astronautas na janela do terminal.
Nós digitamos o seguinte:
jq ".people [] | .name" astro.json
Criação de matrizes e modificação de resultados
Podemos usar jq
para criar novos objetos, como arrays. Neste exemplo, vamos extrair três valores e criar uma nova matriz que contém esses valores. Observe que os [
colchetes de abertura ( ) e de fechamento ( ]
) também são o primeiro e o último caracteres na string de filtro.
Nós digitamos o seguinte:
jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json
A saída é colocada entre colchetes e separada por vírgulas, tornando-a uma matriz formada corretamente.
Os valores numéricos também podem ser manipulados à medida que são recuperados. Vamos extrair do timestamp
arquivo de posição ISS e extraí-lo novamente e alterar o valor que é retornado.
Para fazer isso, digitamos o seguinte:
jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json
Isso é útil se você precisar adicionar ou remover um deslocamento padrão de uma matriz de valores.
Vamos digitar o seguinte para nos lembrar do que o iss.json
arquivo contém:
jq. iss.json
Digamos que queremos nos livrar do message
par chave: valor. Não tem nada a ver com a posição da Estação Espacial Internacional. É apenas uma bandeira que indica que o local foi recuperado com sucesso. Se for excedente aos requisitos, podemos dispensá-lo. (Você também pode simplesmente ignorá-lo.)
Podemos usar jq
a função delete de,, del()
para deletar um par chave: valor. Para excluir o par chave: valor da mensagem, digitamos este comando:
jq "del (.message)" iss.json
Observe que isso não o exclui realmente do arquivo “iss.json”; apenas o remove da saída do comando. Se você precisar criar um novo arquivo sem o message
par chave: valor nele, execute o comando e redirecione a saída para um novo arquivo.
Objetos JSON mais complicados
Vamos recuperar mais alguns dados da NASA. Desta vez, usaremos um objeto JSON que contém informações sobre locais de impacto de meteoros de todo o mundo. Este é um arquivo maior com uma estrutura JSON muito mais complicada do que aqueles com os quais lidamos anteriormente.
Primeiro, digitaremos o seguinte para redirecioná-lo para um arquivo chamado “strikes.json”:
curl -s https://data.nasa.gov/resource/y77d-th95.json | jq. > strikes.json
Para ver a aparência do JSON, digitamos o seguinte:
menos strikes.json
Conforme mostrado abaixo, o arquivo começa com um colchete de abertura ( [
), então o objeto inteiro é um array. Os objetos na matriz são coleções de pares chave: valor e há um objeto aninhado chamado geolocation
. O geolocation
objeto contém mais pares chave: valor e uma matriz chamada coordinates
.
Vamos recuperar os nomes das colisões de meteoros do objeto na posição de índice 995 até o final da matriz.
Vamos digitar o seguinte para canalizar o JSON por meio de três filtros:
jq ". [995:] |. [] | .name" strikes.json
Os filtros funcionam das seguintes maneiras:
.[995:]
: Isso dizjq
para processar os objetos do índice de matriz 995 até o final da matriz. Nenhum número após os dois pontos (:
) é o que indicajq
para continuar até o final da matriz..[]
: Este iterador de array dizjq
para processar cada objeto no array..name
: Este filtro extrai o valor do nome.
Com uma pequena mudança, podemos extrair os últimos 10 objetos do array. Um “-10” instrui jq
para iniciar o processamento de objetos 10 de volta a partir do final da matriz.
Nós digitamos o seguinte:
jq ". [- 10:] |. [] | .name" strikes.json
Assim como nos exemplos anteriores, podemos digitar o seguinte para selecionar um único objeto:
jq ". [650] .name" strikes.json
Também podemos aplicar o fatiamento às cordas. Para fazer isso, digitaremos o seguinte para solicitar os primeiros quatro caracteres do nome do objeto no índice de matriz 234:
jq ". [234] .name [0: 4]" strikes.json
Também podemos ver um objeto específico em sua totalidade. Para fazer isso, digitamos o seguinte e incluímos um índice de matriz sem nenhuma chave: filtros de valor:
jq ". [234]" strikes.json
Se quiser ver apenas os valores, você pode fazer a mesma coisa sem os nomes das chaves.
Para nosso exemplo, digitamos este comando:
jq ". [234] []" strikes.json
Para recuperar vários valores de cada objeto, nós os separamos com vírgulas no seguinte comando:
jq ". [450: 455] |. [] | .name, .mass" strikes.json
Se você deseja recuperar valores aninhados, você deve identificar os objetos que formam o “caminho” para eles.
Por exemplo, para fazer referência aos coordinates
valores, temos que incluir a matriz abrangente, o geolocation
objeto aninhado e a coordinates
matriz aninhada , conforme mostrado abaixo.
Para ver os coordinates
valores do objeto na posição de índice 121 da matriz, digitamos o seguinte comando:
jq ". [121] .geolocation.coordinates []" strikes.json
A função comprimento
A jq
length
função fornece métricas diferentes de acordo com o que foi aplicado, como:
- Strings : o comprimento da string em bytes.
- Objetos : o número de pares chave: valor no objeto.
- Matrizes : o número de elementos da matriz na matriz.
O comando a seguir retorna o comprimento do name
valor em 10 dos objetos na matriz JSON, começando na posição de índice 100:
jq ". [100: 110] |. []. nome | comprimento" strikes.json
Para ver quantos pares chave: valor estão no primeiro objeto da matriz, digitamos este comando:
jq ". [0] | comprimento" strikes.json
As teclas de função
Você pode usar a função keys para descobrir mais sobre o JSON com o qual precisa trabalhar. Ele pode dizer quais são os nomes das chaves e quantos objetos existem em uma matriz.
Para encontrar as chaves do people
objeto no arquivo “astro.json”, digitamos este comando:
jq ".people. [0] | keys" astro.json
Para ver quantos elementos estão na people
matriz, digitamos este comando:
jq ".people | keys" astro.json
Isso mostra que há seis elementos da matriz de deslocamento zero, numerados de zero a cinco.
A função has ()
Você pode usar a has()
função para interrogar o JSON e ver se um objeto tem um nome de chave específico. Observe que o nome da chave deve ser colocado entre aspas. Vamos envolver o comando de filtro entre aspas simples ( '
), da seguinte maneira:
jq '. [] | has ("nametype") 'strikes.json
Cada objeto na matriz é verificado, conforme mostrado abaixo.
Se você quiser verificar um objeto específico, inclua sua posição de índice no filtro de matriz, da seguinte maneira:
jq '. [678] | has ("nametype") 'strikes.json
Não chegue perto do JSON sem ele
O jq
utilitário é o exemplo perfeito de software profissional, poderoso e rápido que torna a vida no mundo Linux um prazer.
Esta foi apenas uma breve introdução às funções comuns deste comando – há muito mais do que isso. Certifique-se de verificar o manual jq abrangente se quiser se aprofundar.