Executando verificação de segurança...
24

Como ler e manipular um JSON

JSON significa "JavaScript Object Notation" (Notação de Objetos JavaScript). Apesar de ter "JavaScript" no nome, ele pode ser usado em outras linguagens, pois o JSON é na verdade um formato para representar dados. A sua sintaxe foi inspirada no JavaScript - daí o nome - mas hoje em dia várias linguagens possuem algum suporte para ele, seja nativo ou via bibliotecas.

Dentro de um JSON podemos ter vários tipos de dados diferentes. Para começar, temos os tipos "básicos":

  • strings: quaisquer caracteres entre aspas duplas. Ex: "sou uma string". Também são aceitas aquelas sequências de escape comuns em várias linguagens, como \n para representar a quebra de linha, \t para TAB, etc.
  • números: pode ser um valor inteiro positivo (42), negativo (-10), com casas decimais (12.34 - e repare que o ponto - e não a vírgula - é o separador das casas decimais) ou em notação científica (2.53962e15)
  • valores booleanos (true e false) e o valor nulo (null)

Além destes, podemos ter também arrays e objetos.

Array

Um array é uma lista ordenada de valores, dentro de colchetes ([ e ]) e separados por vírgulas. Exemplo:

[ 1, "segundo elemento", 3.4 ]

Este array tem 3 elementos: o primeiro é o número 1, o segundo é a string "segundo elemento" e o terceiro é o número 3.4. Veja que ele começa com [ e termina com ], e os elementos são separados por vírgulas.

Um detalhe importante é que um array não precisa ter necessariamente todos os elementos do mesmo tipo. Além disso, a ordem é importante. Então [ 1, "segundo elemento", 3.4 ] não é o mesmo que [ 1, 3.4, "segundo elemento" ].

Objeto

Um objeto é um conjunto de pares "chave: valor", dentro de chaves ({ e }) e separados por vírgulas. A chave sempre é uma string, e o valor pode ser qualquer tipo válido. Exemplo:

{ "nome": "Fulano", "idade": 90 }

Este objeto possui duas chaves:

  • "nome", cujo valor é a string "Fulano"
  • "idade", cujo valor é o número 90

Repare que ele começa com { e termina com }. Entre uma chave e seu respectivo valor, sempre tem o caractere :, e entre cada par "chave: valor" há uma vírgula.

Um detalhe importante é que em um objeto a ordem dos pares "chave: valor" não importa. Ou seja, { "nome": "Fulano", "idade": 90 } e { "idade": 90, "nome": "Fulano" } são exatamente o mesmo objeto.


Dentro de um array ou objeto, os valores podem ser qualquer um dos tipos acima. Ou seja, podemos ter não só os tipos básicos, mas também outros arrays e objetos. Então podemos ter coisas como:

{
    "nome": "Fulano",
    "idade": 90,
    "filmes_preferidos": [ "Pulp Fiction", "Clube da Luta" ],
    "contatos": {
        "telefone": "(11) 91111-2222",
        "emails": [ "[email protected]", "[email protected]" ]
    }
}

O JSON acima é um objeto, pois está delimitado por { e }. Dentro dele temos 4 chaves:

  • "nome", cujo valor é a string "Fulano"
  • "idade", cujo valor é o número 90
  • "filmes_preferidos", cujo valor é um array (pois está delimitado por colchetes: [ e ])
    • este array, por sua vez, possui dois elementos: as strings "Pulp Fiction" e "Clube da Luta"
  • "contatos", cujo valor é outro objeto (pois este também está delimitado por { e })
    • este objeto, por sua vez, possui duas chaves:
      • "telefone": cujo valor é a string "(11) 91111-2222"
      • "emails": cujo valor é um array (pois está delimitado por colchetes)

Outro exemplo:

[
    {
        "nome": "Fulano",
        "idade": 90
    },
    {
        "nome": "Ciclano",
        "idade": 10,
        "email": "[email protected]"
    }
]

O JSON acima é um array, pois está delimitado por [ e ]. Ele contém 2 elementos:

  • o primeiro elemento é um objeto (pois está delimitado por { e }), que por sua vez possui duas chaves:
    • "nome", cujo valor é a string "Fulano"
    • "idade", cujo valor é o número 90
  • o segundo elemento também é um objeto (pois está delimitado por { e }), que por sua vez possui três chaves:
    • "nome", cujo valor é a string "Ciclano"
    • "idade", cujo valor é o número 10
    • "email", cujo valor é a string "[email protected]"

Repare que depois do fechamento do primeiro objeto (ou seja, depois do }) há uma vírgula, que é o separador de elementos de um array. Da mesma forma que um array de números teria a vírgula entre cada número (como por exemplo [ 1, 2, 3 ]), um array de objetos também deve ter a vírgula separando-os.

Regra básica para ler um JSON

A regra básica é: antes de sair escrevendo código, pare e olhe para o JSON. Veja se ele é um objeto ou array (que é o que a grande maioria das APIs REST retorna hoje em dia): se começa com { é um objeto, se começa com [ é um array.

Depois veja o que tem dentro desse objeto ou array. Se for um array, procure pelas vírgulas que separam os elementos e verifique os seus tipos, e preocupe-se com a ordem em que eles estão, para saber como obter cada um. Se for um objeto, veja quais são as chaves e os respectivos valores.

Se o JSON é muito grande e/ou com muitas estruturas aninhadas (objetos dentro de arrays, dentro outros arrays, dentro de outros objetos, etc), formatá-lo ajuda a visualizar a sua estrutura. Há vários sites como esse, que validam e formatam um JSON. Além disso, as linguagens/APIs que trabalham com JSON geralmente possuem uma opção para formatá-lo (veremos alguns exemplos mais abaixo).

Outro ponto importante é verificar se o JSON que você está tentando ler é de fato um JSON válido. Um exemplo clássico é:

{
    "nome": "Fulano",
    "idade": 90
},
{
    "nome": "Ciclano",
    "idade": 10
}

Inicialmente você pode pensar que, como começa com {, então é um objeto. Mas repare bem: o primeiro objeto (o que tem "nome": "Fulano") termina na quarta linha (onde há o }), e logo em seguida há uma vírgula. Logo depois, temos outro objeto (o que tem "nome": "Ciclano").

Este não é um JSON válido. Há dois objetos "soltos", separados por vírgula. Cada um deles individualmente é um JSON válido, mas ter ambos separados por vírgula não é.

O que poderia deixar esta estrutura válida é colocá-la entre [ e ]:

[
  {
    "nome": "Fulano",
    "idade": 90
  },
  {
    "nome": "Ciclano",
    "idade": 10
  }
]

Pois agora ela se tornou um array com dois objetos. Mas da forma que estava (sem o [ e ]), a estrutura é inválida, e muitas APIs dão erro ao tentar processá-la (ou ignoram o segundo objeto, varia conforme a implementação).

Depois de ler o JSON e verificar se ele é um array ou objeto, você pode acessar seus elementos. Cada linguagem fornece seu mecanismo para tal, e abaixo coloquei exemplos em algumas linguagens.


Antes de prosseguir, um detalhe importante: JSON é um formato para troca de dados, ou seja, para informações que serão mandadas "para lá e para cá". E no fim, tudo isso vira texto (um monte de caracteres que você vai ter que interpretar). O que quer dizer que todos os exemplos de JSON que vimos até agora são na verdade strings (ou o resultado retornado por uma API, ou um arquivo).

Se eu consulto uma API e ela retorna { "nome": "Fulano", "idade": 90 }, o que eu terei é uma string que contém o caractere {, depois o espaço, depois a aspas, depois a letra n e assim por diante. O que as APIs de JSON fazem é transformar esta string em alguma estrutura similar na linguagem em questão (geralmente arrays/listas e maps/dicionários/objects, além dos tipos numéricos e strings da própria linguagem). Sempre veja a documentação para saber como é feito o mapeamento entre tipos.

Agora sim, vamos ao código.

Java

No Java existem várias APIs disponíveis, como o pacote org.json e a biblioteca Gson do Google. Todas são bem parecidas, possuindo classes para representar objetos e arrays.

Com a API Gson, podemos mapear o JSON para um objeto específico. Por exemplo:

import java.util.List;
import java.util.Map;
import com.google.gson.annotations.SerializedName;

public class Pessoa {
    private String nome;
    private int idade;
    @SerializedName("filmes_preferidos")
    private List<String> filmesPreferidos;
    private Map<String, Object> contatos;
    // construtores, getters, setters, etc...
}

Com isso, podemos ler o JSON e criar uma instância de Pessoa com os campos já preenchidos:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

String texto = "{ \"nome\": \"Fulano\", \"idade\": 90, \"filmes_preferidos\": [ \"Pulp Fiction\", \"Clube da Luta\" ],"
                + " \"contatos\": { \"telefone\": \"(11) 91111-2222\", \"emails\": [ \"[email protected]\", \"[email protected]\" ]  } }";
Gson gson = new GsonBuilder().setPrettyPrinting().create();
// lê o JSON e cria uma Pessoa com os campos preenchidos
Pessoa pessoa = gson.fromJson(texto, Pessoa.class);
System.out.println("nome=" + pessoa.getNome());
for (String filme : pessoa.getFilmesPreferidos()) {
    System.out.println(filme);
}
for (Map.Entry<String, Object> contato : pessoa.getContatos().entrySet()) {
    System.out.println(contato.getKey() + "=" + contato.getValue());
}
// adicionar um novo contato
pessoa.getContatos().put("twitter", "@fulano");
System.out.println(gson.toJson(pessoa));

Com isso fica bem mais fácil obter os campos do JSON e adicionar novas informações ao mesmo. A saída é:

nome=Fulano
Pulp Fiction
Clube da Luta
telefone=(11) 91111-2222
emails=[[email protected], [email protected]]
{
  "nome": "Fulano",
  "idade": 90,
  "filmes_preferidos": [
    "Pulp Fiction",
    "Clube da Luta"
  ],
  "contatos": {
    "telefone": "(11) 91111-2222",
    "emails": [
      "[email protected]",
      "[email protected]"
    ],
    "twitter": "@fulano"
  }
}

Claro que, dependendo da situação, nem sempre é desejável mapear o JSON para uma classe. Pode ser que sejam muitos dados e você não precise de todos, então não vale a pena mapear tudo, por exemplo. Neste caso, você pode usar um java.util.Map:

Map json = gson.fromJson(texto, Map.class);

Assim, o JSON vira um Map, sendo que as chaves são Strings e os valores podem ser List, Map, String, Double, etc, dependendo do que eles eram no JSON original. Para obter o telefone, por exemplo, você poderia fazer ((Map) json.get("contatos")).get("telefone"). Não é muito "bonito", mas se você só precisa deste campo, e o JSON é muito grande e complexo, talvez não valha a pena mapeá-lo para uma classe só para obter um único campo.

Além disso, a classe Gson possui métodos que trabalham com java.io.Reader e java.io.Writer, permitindo que o JSON seja lido e escrito de/para arquivos e quaisquer outros streams de dados diretamente.

Se o JSON for inválido, o método fromJSON lança um com.google.gson.JsonSyntaxException.

C#

Em C# existem várias APIs diferentes para manipular JSON, como você pode ver neste comparativo. Pelo que vi, são todas bem parecidas em termos de funcionamento básico. Segue abaixo um exemplo com o Json.NET:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

string texto = "{ \"nome\": \"Fulano\", \"idade\": 90, \"filmes_preferidos\": [ \"Pulp Fiction\", \"Clube da Luta\" ],"
                + " \"contatos\": { \"telefone\": \"(11) 91111-2222\", \"emails\": [ \"[email protected]\", \"[email protected]\" ]  } }";
dynamic dobj = JsonConvert.DeserializeObject<dynamic>(texto);
// obter nome
Console.WriteLine(dobj["nome"].ToString());
// percorrer array de filmes
foreach (string filme in dobj["filmes_preferidos"])
{
    Console.WriteLine(filme);
}
// percorrer contatos
foreach (JProperty c in dobj["contatos"])
{
    Console.WriteLine($"{c.Name} = {c.Value}");
}
// incluir novo contato
dobj["contatos"]["twitter"] = "@fulano";
Console.WriteLine(JsonConvert.SerializeObject(dobj, Formatting.Indented));

Fazemos algo parecido com o código anterior em Java: imprimimos o nome, depois percorremos o array de filmes e por fim os contatos. Depois criamos um novo contato.

Se o JSON for inválido, é lançado um Newtonsoft.Json.JsonReaderException. Veja mais exemplos com outras bibliotecas aqui.

Python

Em Python, você pode usar o módulo json. A conversão de tipos do JSON de/para o Python é feita com base nesta tabela:

JSONPython
objectdict
arraylist
stringstr
number (int)int
number (real)float
trueTrue
falseFalse
nullNone

Portanto, objetos JSON serão transformados em dicionários, e arrays serão transformados em listas. Com isso fica fácil manipular o JSON:

import json

texto = '{ "nome": "Fulano", "idade": 90, "filmes_preferidos": [ "Pulp Fiction", "Clube da Luta" ], "contatos": { "telefone": "(11) 91111-2222", "emails": [ "[email protected]", "[email protected]" ] } }'
obj = json.loads(texto)
# obter o nome
print(obj['nome'])
# percorrer o array de filmes
for filme in obj['filmes_preferidos']:
    print(filme)
# percorrer as chaves e valores do objeto "contatos"
for tipo, contato in obj['contatos'].items():
    print('{}={}'.format(tipo, contato))

# incluir um novo contato
obj['contatos']['twitter'] = '@fulano'
# imprimir o json (indentando com 2 espaços)
print(json.dumps(obj, indent=2))

Para transformar uma string que contém um JSON nos objetos correspondentes do Python, basta usar o método loads. Como o JSON é um objeto, o retorno de loads é um dicionário, que podemos manipular normalmente, como qualquer outro dicionário do Python. O mesmo vale para os seus valores. A chave "filmes_preferidos", por exemplo, contém um array, que o método loads transforma em uma lista. Já a chave "contatos" possui um objeto, e portanto ela se torna um dicionário. Para percorrê-los, usamos as formas tradicionais da linguagem, como faríamos com qualquer outra lista ou dicionário.

Para modificar o JSON, é a mesma coisa: basta manipular as listas e dicionários normalmente, adicionando, removendo ou mudando seus elementos. Por fim, podemos gerar uma string contendo todo o JSON, usando o método dumps. Usei o parâmetro indent=2 para que o JSON fique bem formatado, usando 2 espaços para indentação.

Existem ainda os métodos load e dump, que em vez de ler e escrever o JSON de/para strings, faz a leitura e escrita em arquivos.

Se o JSON for inválido, os métodos loads e load lançam um JSONDecodeError.

PHP

Em PHP, você pode usar a função json_decode para transformar uma string contendo um JSON no objeto correspondente. O primeiro parâmetro é a string contendo o JSON, e o segundo parâmetro (opcional) controla o tipo que será retornado. No exemplo abaixo eu passo true, indicando que o retorno será um array associativo:

$texto = <<<JSON
{
  "nome": "Fulano", "idade": 90, "filmes_preferidos": [ "Pulp Fiction", "Clube da Luta" ],
  "contatos": { "telefone": "(11) 91111-2222", "emails": [ "[email protected]", "[email protected]" ] }
}
JSON;
// converte o texto para um array
$json = json_decode($texto, true);
// obter o nome
echo $json['nome'], PHP_EOL;
// percorrer o array de filmes
foreach ($json['filmes_preferidos'] as $filme) {
    echo $filme, PHP_EOL;
}
# percorrer os contatos
foreach ($json['contatos'] as $tipo => $contato) {
    echo "{$tipo} = ". (is_array($contato) ? implode(", ", $contato) : $contato), PHP_EOL;
}
// adicionar novo contato
$json['contatos']['twitter'] = '@fulano';
// imprimir o JSON formatado
echo json_encode($json, JSON_PRETTY_PRINT);

Com isso, cada chave do objeto JSON se torna uma chave no array associativo, e seus respectivos valores podem ser números, strings ou outros arrays. E para transformar esse array em uma string contendo todo o JSON, eu uso a função json_encode. E também uso a opção JSON_PRETTY_PRINT para que ele fique formatado.

Se $texto contém um JSON inválido, json_decode retorna NULL.

JavaScript

Em JavaScript, existe o objeto JSON, que possui os métodos parse (para transformar uma string contendo todo o JSON na estrutura correspondente do JavaScript - ou seja, um array, objeto, string, número, etc), e stringify, que faz o oposto (transforma uma variável do JavaScript em uma string no formato JSON).

let texto = '{ "nome": "Fulano", "idade": 90, "filmes_preferidos": [ "Pulp Fiction", "Clube da Luta" ], "contatos": { "telefone": "(11) 91111-2222", "emails": [ "[email protected]", "[email protected]" ] } }';

let json = JSON.parse(texto);
// imprimir nome
console.log(json.nome);
// imprimir filmes
json.filmes_preferidos.forEach(filme => console.log(filme));
// imprimir contatos
for (let [tipo, contato] of Object.entries(json.contatos)) {
    console.log(tipo, '=', Array.isArray(contato) ? contato.join() : contato);
}
// adicionar novo contato
json.contatos.twitter = '@fulano';
// transformar em string (indentado com 2 espaços)
console.log(JSON.stringify(json, null, 2));

Um objeto JSON se torna um objeto do JavaScript, e podemos acessar suas chaves diretamente (como fizemos com json.nome e json.contatos, por exemplo). Já um array JSON se torna um array do JavaScript, com todos os métodos que este possui, como o forEach utilizado acima.

Por fim, JSON.stringify possui uma opção para formatar a saída, bastando passar a quantidade de espaços usados para indentação.

Se o JSON for inválido, JSON.parse lança um SyntaxError.


Com isso, acredito que agora você já está mais preparado para ler e manipular um JSON. Não é tão difícil: o grande problema - que pelo menos eu vejo - é que as pessoas não param para analisar a estrutura e já saem escrevendo código sem pensar. Se você olha a estrutura antes, consegue ver como percorrê-la, um nível de cada vez, até chegar na informação que você precisa.


Baseado neste post do meu blog - tive que cortar algumas partes porque o TabNews limita o post a 20000 caracteres, então não deixe de conferir o post completo lá :-)

E como complemento, vale lembrar que arrays e objetos, apesar de serem as escolhas mais comuns, não são as únicas - ver mais em "O que é JSON? Para que serve e como funciona?".

Carregando publicação patrocinada...
4

A simplicidade do JSON e sua integração perfeita com objetos e arrays JavaScript são, de fato, muito belas. No entanto, é aqui que reside um problema fundamental. Frequentemente usamos o JSON não apenas como um meio de transmitir dados, mas também como um veículo para carregar instruções sobre o processamento destes.

Esse duplo propósito desfoca a linha entre a representação de dados e as regras para a transformação de dados, normalmente definidas em uma linguagem de domínio específico. Já que na maioria dos casos ninguém lê ativamente o JSON, como mencionado, uma abordagem mais eficiente seria simplemente transferir os dados em formatos binários e oferecer uma interface para acessar estes dados mantendo garantindo que todas as regras são mantidas.

Essa abordagem pode parecer arcaica ou excessivamente complexa para alguns, sugerindo uma mentalidade antiquada em uma era de desenvolvimento rápido e práticas de codificação '"agéis"', mas é crucial abordar o JSON com um senso de cautela. Enquanto é inegavelmente universal e conveniente, especialmente em JavaScript, há momentos em que outras alternativas podem ser mais apropriadas.

O XML, por exemplo, apesar de ser considerado por muitos como ultrapassado, ainda tem um papel crucial em sistemas onde a estrutura de dados é complexa e a definição de regras e esquemas é essencial e é uma escolha robusta para interoperabilidade e validação rigorosa de dados.

Protobufs, por outro lado, oferece uma abordagem eficiente e compacta para a serialização de dados estruturados. Sua natureza binária o torna particularmente adequado para comunicação entre serviços em ambientes de distribuidos, onde a eficiência é uma prioridade.

Mas eis que surge jq, realocando o JSON de volta ao seu lugar como texto, mais especificamente como pate da strem de texto no pipeline do Unix. jq permite a manipulação elegante e eficiente de dados JSON diretamente da linha de comando, transformando JSON não apenas em um formato de dados, mas em um terreno de brincadeiras (e soluçoões robustas) baseadas na arte do processamento de stream no pipeline do unix. Estou me divertindo com isso e volto para contar as aventuras!

3

Eu não toquei nesse ponto porque achei que ia desviar do assunto principal (entender o formato), e também porque o texto está quase no limite de caracteres que o site permite, mas enfim, concordo com vc :-)

De fato existe um abuso de JSON. Usa-se pra tudo, mas nem sempre ele é a melhor opção (e não duvido que tenha gente que ache que é a única).

Aliás, incrível como isso é recorrente na nossa área. Qualquer coisa que se populariza começa a ser usada pra tudo, até pra casos em que não serve e para os quais existem soluções melhores. Infelizmente também ocorreu isso com JSON.

Nem tudo precisa ser "human readable" (uma das supostas vantagens do JSON, que pra mim é um grande "depende"). Muitas vezes um formato binário, como o protobuf que vc citou, é mais adequado. O ideal seria que todos conhecessem diferentes formatos, seus prós e contras, em quais casos um faz mais sentido que outro e escolher o mais adequado de acordo com o contexto (como aliás, deveria ser pra tudo em computação).

2

Na verdade quase nunca precisa ser legível, e em alguns casos, nem deveria (ainda que não pode ser confundido com dar segurança).

Eu acho incrível especialmente pessoas experientes adotando algo para tudo e/ou sem questionar. Inexperientes isso é normal, ainda que não desejável. Daí começo questionar a experiência delas.

3

Bom como sempre.

A do C# está bem desatualizada. Agora tem uma API nova padrão na biblioteca padrão absurdamente mais rápida porque usa gereção de código e funciona até no modelo AOT. E tem o "tutorial de doc". E para ver diferenças de versões anteriores. E para ver as inúmeras novidades lançadas agora. Performance (sem a última versão):

Benchmark JSON

Mais sobre performance.

Assim essa posição entre os 50 mais já irá para os primeiros lugares na atualização do Techempower, como já é (estava e talvez volte para o 1o. lugar) na avaliação que só considera a linguagem de forma mais pura. Eles estão trabalhando porque querem os primeiros lugares em todas as avaliações, como o que muitos gostam, o Fortunes (está na melhor posição da história e a próxima atualização já é certo que estará melhor ainda, imagine com o .NET 9 que sai ano que vem e o TE r24).

Espero ter ajudado.


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).

1

Maniero e seu C Sharp. É realmente ele está constantemente em atualização. Ultima vez que usei C Sharp foi em 2011, preciso fazer um projeto em C Sharp ( não que precise necessariamente mas como é Desktop acredito que seja o ideal ) muita coisa mudou para melhor. A única reclamação ainda é eu não gostar muito do Visual Studio até hoje ( minha máquina é lenta demais pra uma versão mais nova também ).