Skip to main content

JSON

JSON, que significa "JavaScript Object Notation" (Notação de Objetos JavaScript), é um formato de dados leve e amplamente utilizado para troca de informações entre sistemas. Ele se tornou popular devido à sua simplicidade, legibilidade e fácil interpretação tanto por humanos quanto por máquinas. O JSON foi criado antes do YAML, mas depois do XML.

Foi criado para preencher uma necessidade específica na comunicação entre sistemas distribuídos, especialmente na web. Antes do JSON, os desenvolvedores frequentemente usavam formatos de dados como XML para trocar informações entre aplicativos web e servidores. No entanto, XML, embora seja poderoso e flexível, tem uma sintaxe relativamente pesada e é mais complicado de analisar e gerar em comparação com outros formatos de dados mais simples.

Aqui estão algumas características importantes do JSON:

  • Sintaxe Simples: O JSON usa uma sintaxe simples de chave-valor para representar dados. Os dados são organizados em pares chave-valor, onde a chave sempre é uma string e o valor pode ser um número, string, booleano, array, objeto ou null.

  • Independente de Linguagem: O JSON é independente de linguagem, o que significa que pode ser lido e interpretado por uma ampla variedade de linguagens de programação. Como resultado, é frequentemente usado para comunicação entre sistemas heterogêneos.

  • Facilmente Interpretável: Devido à sua estrutura simples, o JSON pode ser facilmente interpretado e manipulado por humanos e máquinas. Isso o torna ideal para armazenar e transmitir dados em aplicativos web, serviços web, APIs e muito mais.

  • Suporte Universal: O JSON é suportado nativamente pela maioria das linguagens de programação modernas. Além disso, existem muitas bibliotecas disponíveis para trabalhar com JSON em praticamente qualquer ambiente de desenvolvimento.

  • Ampla Utilização: O JSON é amplamente utilizado em uma variedade de domínios, incluindo desenvolvimento web, integração de sistemas, armazenamento de configurações e muito mais. É comumente usado em APIs RESTful para transmitir dados entre o cliente e o servidor.

Um exemplo de um arquivo JSON especificando uma lista de servidores com dois servidores.

arquivo.JSON

{
"servers": [
{
"name": "Server1",
"status": "active",
"ip": "192.168.0.10"
},
{
"name": "Server1",
"status": "active",
"ip": "192.168.0.10"
}
]
}

JSON não se importa com o recuo de espaços e a formatação como no YAML. Isso o torna muito popular para envio de informações. Abaixo temos a mesma coisa que acima. Isso não seria possível com o YAML. Para isso o JSON precisa delimitar o início e o fim de cada bloco, chave-valor e lista por isso usa {}, [], e ,.

Isso é a mesma coisa que o exemplo acima.

{"servers": [{"name":"Server1","status":"active","ip":"192.168.0.10"},{"name":"Server1","status":"active","ip":"192.168.0.10"}]}

Se estivessemos enviando uma mensagem vazia de um sistema para outro seria assim.

{}

Com isso podemos entender que tudo começa com o . A partir disso podemos começar a declarar nossas chaves e valores.

Aqui um simples exemplo de como usar chave-valor. Vamos imaginar que queremos informações sobre david. Esse JSON somente traz informações sobre 1 pessoa.

todas as chaves que são strings precisam estar entre "" e todos os valores que são string também, mas números e booleanos não.

Observe que a cada fechamento de par chave-valor temos uma vírgula para separar a próximo conjunto chave-valor. Essa abordagem elimina a necessidade de espaçamentos como no formato YAML. Quando se perguntar, qual o proximo? têm uma vírgula ali.

{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true
}

As listas no JSON ficam entre []. Cada posição da lista pode ser objeto (outra lista ou dicionário) ou valores. O único caso que temos o uso de [] é na lista todo o resto é . Se a lista for somente com valores, pode ser passado diretamente os valores.

Para representar uma lista de nomes de pessoas.

{
"names": [
"david",
"maria",
"carlos",
]
}

Vale lembrar que uma lista é ordenada logo o exemplo abaixo é diferente do exemplo acima.

{
"names": [
"maria",
"carlos",
"david",
]
}

Também podemos ter um dicionário/map, ou seja, diferentes dados sobre o elemento: Vamos transformar o primeiro exemplo em um dicionaŕio e vamos incluir uma lista de telefone. Também já vamos incluir um outro dicionário dentro deste.

{
"david": {
"age": 38,
"status": "studing",
"position": "devsecops",
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
}
}

A ordem dos parâmetros dentro do bloco não importa. Sempre buscamos um valor pela chave e não pela posição. O exemplo a seguir é a mesma coisa que o exemplo acima.

{
"david": {
"position": "devsecops",
"status": "studing",
"address": {
"city": "vila velha",
"state": "ES"
},
"age": 38,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
]
}
}

Quando estamos criando uma lista pegamos um conjunto completo. O exemplo acima não é bom, vamos generalizar para servir para qualquer pessoa.

{
"person": {
"name": "david",
"age": 38,
"status": "studing",
"working": true,
"position": "devsecops",
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
}
}

Agora podemos ter uma lista de pessoas que são dicionários/map. Dentro de cada dicionário temos mais lista e mais dicionário.

{
"person": [
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
},
{
"name": "carlos",
"age": 52,
"status": "drinking",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
"address": {
"city": "ribeirao preto",
"state": "SP"
}
}
]
}

Se quiser entender melhor o fluxo do compilador para que o json usa veja o https://www.json.org/json-en.html.

JSON vs YAML

Existem vários formatadores na internet para transformar JSON em YAML e vice versa.

Convert YAML para JSON Convert JSON para YAML

  • Sintaxe

    • YAML tem uma sintaxe mais compacta e expressiva, usando recuo e espaçamento para definir a estrutura dos dados.
    • JSON usa uma sintaxe mais simples baseada em chaves e valores separados por dois pontos e arrays e objetos definidos por colchetes e chaves, respectivamente.
  • Legibilidade:

    • YAML é conhecido por ser mais legível para humanos devido à sua sintaxe mais natural e uso de recuo para indicar a hierarquia dos dados.
    • JSON também é legível, mas sua sintaxe pode ser considerada mais densa em comparação com YAML, especialmente quando os objetos e arrays são aninhados.
  • Uso:

    • YAML é frequentemente usado em configurações de aplicativos, como arquivos de configuração, devido à sua legibilidade e capacidade de representar estruturas complexas de forma concisa.
    • JSON é comumente utilizado em APIs web para troca de dados entre sistemas, devido à sua simplicidade e ampla compatibilidade com a maioria das linguagens de programação.
  • Expressividade:

    • YAML tende a ser mais expressivo, permitindo uma representação mais natural e fácil de entender de dados estruturados, o que pode resultar em arquivos YAML mais compactos em comparação com JSON para a mesma estrutura de dados.
    • JSON é mais estrito em sua sintaxe e estrutura, o que pode torná-lo menos expressivo em alguns casos, mas também mais previsível e menos sujeito a ambiguidades.
  • Suporte para Comentários:

    • YAML suporta comentários, o que pode ser útil para fornecer contexto ou documentação dentro dos arquivos.
    • JSON não suporta comentários oficialmente, embora alguns implementadores permitam comentários em arquivos JSON, isso não é parte da especificação oficial.

JSONPath e JQ

Assim como no SQL podemos criar uma query para filtrar dados em uma tabela, no JSON podemos criar queries para filtrar dados.

Podemos fazer os exemplos usando o site jsonpath.com, mas podemos instalar um binário também que faz exatamente o que site propõe que é o jpath.

Ainda temos o jq que faz as coisas um pouco diferentes, mas com a mesma idéia.

Vamos entender as diferenças e quando usar cada uma.

Vamos instalar o jq e o jpath.

sudo apt-get install jq -y
jq --version
jq-1.6

Existem várias clis de jsonpath, mas eu escolhi uma em node que a sentença é a mesma do site. É necessário ter o node instalado.

node --version
v21.5.0

npm install -g jsonpath-cli

E vamos criar um arquivo de exemplo para fazer extração de dados. Use esse exmplo no site se for testar por lá.

echo '{
"person": [
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
},
{
"name": "carlos",
"age": 52,
"status": "drinking",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
"address": {
"city": "ribeirao preto",
"state": "SP"
}
}
],
"car": {
"color": "blue",
"price": "$20,000"
},
"bus": {
"color": "blue",
"price": "$120,000"
},
"testNum": [
25,
33,
44,
1,
39,
56,
96,
16
]
}' > example.json

Já temos o nosso example.json pronto para brincar e vamos entender o JQ.

O jq possui a seguinte sentença.

jq <opções> '<expressão>' <arquivo.json>

Se quiser passar o conteúdo do arquivo direto para o jq também é possível e muito usado por sinal em vários outros comandos.

cat example.json | jq .

Tudo começa com o . como sendo a raíz de tudo Se você esta dizendo todos os dados você quer o .. Não irei colocar algumas saídas para não ficar muito extenso, mas faça os testes.

Iremos andando pelo arquivo usando pontos sendo que cada ponto entra em um bloco.

No site uma vez que o exemplo esta carregado a única diferença que vamos fazer é ao invés da sentença começar com . irá começar com $.

# Vai imprimir tudo
jq '.' example.json

Mas se eu quero só o car.

jq '.car' example.json  
{
"color": "blue",
"price": "$20,000"
}

Se fosse no site veja como ficaria.

alt text

Ou usando o jpath instalado anteriormente.

devsecops git:(main)cat example.json| jpath "$.car"  
[
{
"color": "blue",
"price": "$20,000"
}
]

Se eu quiser só a cor do car.

jq '.car.color' example.json
"blue"

Agora temos uma idéia de como navegar entre os dados. Porém person é uma lista e sabemos que lista tem vários elementos começando na posição 0.

# Se person é uma lista, ele tem que me trazer uma lista. Observe como começou o [ e terminou com ].
jq '.person' example.json
[
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
},
{
"name": "carlos",
"age": 52,
"status": "drinking",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
"address": {
"city": "ribeirao preto",
"state": "SP"
}
}
]

Mas eu quero só o 1 elemento da lista, o primeiro e o primeiro é o [0]

# O resultado não é uma lista, é um elemento da lista, logo temos um bloco que é um dicionário/map
jq '.person[0]' example.json
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
}

# Elemento 0 e 1 da lista.
jq '.person[0,1]' example.json
jq '.person[0].name' example.json
"david"

Se eu quiser o telefone da casa eu sei que está no bloco telefone, mas telefone é uma lista, porém esta na posição 1, pois zero é a primeira.

# Se eu trazer a posição temos o bloco, mas dentro do bloco temos o telefone na chave home.
jq '.person[0].telephone[1]' example.json
{
"home": 551234567890
}
jq '.person[0].telephone[1].home' example.json
551234567890

Até aqui já deu para ter uma idéia de como navegar em um arquivo JSON, mas também sentimos que precisamos conhecer a estrutura do arquivo para saber onde queremos ir.

O que acontece se eu procurar uma posição que não existe ou um bloco que não existe?

jq '.person[2]' example.json  
null

jq '.teste' example.json
null

Imagine que a lista de pessoas tem vários nomes e você não sabe a posição da pessoa que você quer. Eu quero trazer o bloco completo da pessoa que tem o nome carlos.

Sabemos que se fossemos buscar carlos seria .person[1] mas eu não tenho certeza da ordem. Antes de resolver esse problema vamos entender como aplicar um filtro na saída.

A chave testNum tem uma lista de números.

jq '.testNum' example.json  
[
25,
33,
44,
1,
39,
56,
96,
16
]

jq '.testNum[5]' example.json
56

# Para imprimir as posições 2 3 e 5
jq '.testNum[2,3,5]' example.json
44
1
56

Se temos como imprimir as posições que queremos para gerar uma saída específica, podemos então querer imprimir somente as que tem valores maior do que 30.

A expressão seria .testNum[ Check se cada item na lista é >30].

Com isso poderíamos uma expressão com assim '.testNum[Checar se o item da lista é > 30]'

Checar Se = ?()

Então a expressão fica '.testNum[?(o item da lista é > 30)]'.

o item da lista = @

A expressão fica então '.testNum[?(@ > 30)]'

jq ".testNum[?(@ > 30)]" example.json  
jq: error: syntax error, unexpected '?' (Unix shell quoting issues?) at <top-level>, line 1:
.testNum[?(@ > 30)]
jq: 1 compile error

O JQ não reconhece, mas usando o jpath que utiliza as mesma sintaxe do site.

cat example.json| jpath "$.testNum[?(@ > 30)]"
[
33,
44,
39,
56,
96
]

Embora JSONPath e JQ tenham conceitos semelhantes de navegação e filtragem de dados JSON, eles têm algumas diferenças na sintaxe e funcionalidades específicas.

  • JSONPath: É uma linguagem de consulta para JSON. É usado principalmente em ambientes web e serviços RESTful. JSONPath é uma especificação e pode ser implementado em várias linguagens de programação. No entanto, a sintaxe exata e as funcionalidades disponíveis podem variar entre as implementações. o JSONPath é utilizado nos comandos do kubectl. Entender ele é ganhar poderes.

  • jq: É uma ferramenta de linha de comando para processamento de dados JSON. Ele fornece uma linguagem de consulta poderosa e expressiva para trabalhar com JSON.

Sabendo que a sintaxe é diferente, vamos fazer funcionar no jq.

No jq é possível fazer essas duas expressões.

#Essa te traz o array
jq ".testNum" example.json
[
25,
33,
44,
1,
39,
56,
96,
16
]

# Quando passamos [] depois do array ele traz todos os VALORES no array
# Essa sintaxe não é possivel no jsonpath.
jq ".testNum[]" example.json
25
33
44
1
39
56
96
16

A regra no jsonpath é aplicada dentro de [] por isso não é possível usar o segundo modo.

Sabendo disso, vamos continuar. No linux usamos o | para injetar os dados como inputs. O jq usa a mesma idéia. Vamos injectar os dados na funcao select.

A funcao select (. > 0) novamente voltamos a estaca zero onde o . para ele é o raiz. Se existissem mais coisas dentro do bloco poderíamos continuar a busca, mas como já é onde os dados estão aplicamos a comparação.

# Não funciona, precisa dos valores nào da lista.
jq ".testNum | select (. > 30)" example.json
[
25,
33,
44,
1,
39,
56,
96,
16
]

# Método correto
jq ".testNum[] | select (. > 30)" example.json
33
44
39
56
96

Agora vamos fazer a sentença de procurar a person que tem o nome de carlos.

jq '.person[] | select (.name == "carlos")' example.json
{
"name": "carlos",
"age": 52,
"status": "drinking",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
"address": {
"city": "ribeirao preto",
"state": "SP"
}
}

Se fossemos fazer isso no jsonpath seria:

cat example.json| jpath '$.person[?(@.name == "carlos")]'
# Mesma saída da de cima.

A vantagem de filtrar dessa maneira é que seja usando o jsonpath ou jq é que eu procuro o dado pelo filtro e não pela posição na lista que pode variar.

Se fossemos piorar ainda mais o caso, poderiamos procurar por quem tem o telefone = 5511999998888.

jq '.person[] | select(.telephone[] | .mobile? == 5511999998888 or .home? == 5511999998888)' example.json

{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
}

Se eu não qual o nome do campo, se é mobile ou home ou qualquer outro, eu poderia testar todos

jq '.person[] | select(.telephone[] | to_entries[] | .value == 5511999998888)' example.json
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
}

Se eu quisesse só o nome da pessoa, sabendo que essa é a saída acima podemos só continuar o comando.

jq '.person[] | select(.telephone[] | to_entries[] | .value == 5511999998888) | .name' example.json
"david"

Os dois métodos valem a pena ser estudados. Para uso no linux e manipulação de json usamos muito o JQ. Porém muitas ferramentas clis utilizam dentro delas bibliotecas do jsonpath. Vamos direcionar o foco do estudo agora para jsonpath.

No nosso exemplo temos car e bus ambos tem color. Podemos usar um wildcard como um Any. O jq não funciona com esta sintaxe.

cat example.json| jpath '$.car.color'
[
"blue"
]

cat example.json| jpath '$.bus.color'
[
"black"
]

cat example.json| jpath '$.*.color'
[
"blue",
"black"
]

cat example.json| jpath '$.*.price'
[
"$20,000",
"$120,000"
]

# address é um dicionário e city é uma chave dentro
cat example.json| jpath '$.person[*].address.city'
[
"vila velha",
"ribeirao preto"
]

#Mas telephone é uma lista e queremos todos os telefones do tipo mobile
cat example.json| jpath '$.person[*].telephone[*].mobile'
[
5511999998888,
5511777776666
]

Agora só pra você entender melhor, o wildcard praticamente traz os blocos de um determinado nível. Veja essa sequencia.

# Só quero os blocos de segundo nível
cat example.json| jpath '$.*.*'
[
{
"name": "david",
"age": 38,
"status": "studing",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
"address": {
"city": "vila velha",
"state": "ES"
}
},
{
"name": "carlos",
"age": 52,
"status": "drinking",
"position": "devsecops",
"working": true,
"telephone": [
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
"address": {
"city": "ribeirao preto",
"state": "SP"
}
},
"blue",
"$20,000",
"black",
"$120,000",
25,
33,
44,
1,
39,
56,
96,
16
]
# .car .bus e .person não apareceram e testNum já veio com os valores

#Agora só o de terceiro nível
➜ devsecops git:(main)cat example.json| jpath '$.*.*.*'
[
"david",
38,
"studing",
"devsecops",
true,
[
{
"mobile": 5511999998888
},
{
"home": 551234567890
}
],
{
"city": "vila velha",
"state": "ES"
},
"carlos",
52,
"drinking",
"devsecops",
true,
[
{
"mobile": 5511777776666
},
{
"home": 550987654321
}
],
{
"city": "ribeirao preto",
"state": "SP"
}
]
# Os valores que eram de 2 nível já sumiram

# Quarto nível
➜ devsecops git:(main)cat example.json| jpath '$.*.*.*.*'
[
{
"mobile": 5511999998888
},
{
"home": 551234567890
},
"vila velha",
"ES",
{
"mobile": 5511777776666
},
{
"home": 550987654321
},
"ribeirao preto",
"SP"
]
# Só vieram os telefones

# Quinto nível
➜ devsecops git:(main)cat example.json| jpath '$.*.*.*.*.*'
[
5511999998888,
551234567890,
5511777776666,
550987654321
] # Só os valores dos telefones

# Como não tem sexto nível veio vazio.
➜ devsecops git:(main)cat example.json| jpath '$.*.*.*.*.*.*'
[]

jpath '$.*.*.*.*.mobile'
[
5511999998888,
5511777776666
]

Observer que as chaves nunca vieram, só os valores. A chave e para referencia o valore.

Como já vimos antes, podemos imprimir somente determinadas posições da lista, mas também podemos imprimir desta maneira

# Tudo
cat example.json| jpath '$.testNum[*]'
[
25,
33,
44,
1,
39,
56,
96,
16
]

# Posição de 0 a 3
cat example.json| jpath '$.testNum[0:3]'
[
25,
33,
44
]

# Posição de 0 E 3
cat example.json| jpath '$.testNum[0,3]'
[
25,
1
]

# de 3 A 6
cat example.json| jpath '$.testNum[3:6]'
[
1,
39,
56
]

# Isso é a mesma coisa que imprimir tudo de 0 até o final
cat example.json| jpath '$.testNum[0:]'
[
25,
33,
44,
1,
39,
56,
96,
16
]

# Do início ao fim, mas pulando de 2 em 2
cat example.json| jpath '$.testNum[0::2]'
[
25,
44,
39,
96
]

# Do início ao fim, mas pulando de 2 em 2
cat example.json| jpath '$.testNum[-1:]'
[
16
]

Tenta decifrar isso aqui...

cat example.json| jpath '$.person.*.telephone[0::2].*'
[
5511999998888,
5511777776666
]