JSON: Aprenda para sobreviver

Saudações. Nesse artigo vamos aprender e explorar o formato de dados JSON. Todos os softwares, plataformas online e serviços digitais o utilizam, logo, somos forçados a aprende-lo para sobreviver.

1 – O que é JSON

JSON significa JavaScript Object Notation. É um formato de representação e armazenamento de dados, tanto para humanos (usuários) quanto para computadores (programas) criados na linguagem JavaScript e que se espalhou para todos as linguagens de programação, bancos de dados (JSONB) e aplicativos.

Você não precisa aprender nenhuma linguagem de programação para aprender JSON.

Ele foi concebido para ser simples, ter poucas regras e ser armazenado e transmitido em texto simples.

O bloco de código abaixo é um exemplo dos dados digitados por um usuário em um formulário online e enviado para um site:

JSON
{
    "nome": "Patolino Silva",
    "idade": 22,
    "profissao": "vendedor",
    "data_nascimento": "01/01/2013"
}

JSON é algo tão simples que se você entendeu o bloco acima, você ja sabe 98% do que precisa saber para trabalhar com ele!

Caso deseje aprender de forma mais ortodoxa, a RFC 4627 define todas as regras para uso de JSON:

2 – Tipos de dados

Informações digitais são armazenadas e processadas baseadas no seu tipo, os tipos básicos são:

  • Números inteiros (integer): todos os números positivos ou negativos que são escritos apenas cos caracteres: 0 a 9 e hífen, não podem ser envolvidos por aspas. Exemplos:
    • 0
    • -14
    • 83839283
    • 42
    • -1
  • Números de ponto flutuante (float): todos os números que envolvem frações, porcentagem, números racionais em geral e são escritos com os caracteres 0 a 9, hífen e ponto. Usa-se o ponto no lugar da virgula como divisor. Exemplos:
    • 3.14
    • -14.5
    • 42.708381922
    • -1.1
  • Texto (string): conjunto de caracteres de texto, ASCII e UNICODE, delimitados por aspas duplas. Números envolvidos por aspas serão tratados como se fossem texto, ou precisarão ser convertidos para inteiros no código que recebe o JSON. Exemplos:
    • “O rato roeu a roupa do rei de roma”
    • “0”
    • “42”
    • “42.12345”
    • “pi=3.14”
  • Valor booleano (boolean): representação de um bit, sendo bit 0 para false (desativado, ausente) ou bit 1 para true (ativado, verdadeiro). Valores admitidos somente em minúsculo (não use T ou F maiúsculo como no Python):
    • true
    • false
  • Lista (array): é um conjunto de valores com posições numéricas sequenciais iniciando no índice zero. Uma lista é iniciada com o caracter “[” e finalizada com o caracter “]”, os itens são separados por vírgula. O primeiro elemento de uma lista está no índice 0, e se uma lista tem 100 elementos o último terá o índice 99. Exemplos:
    • [ “O rato roeu a roupa do rei de roma”, 42, true, 3.14, “42.123456” ]
    • [ “terror”, “suspense”, “comédia”, “documentario” ]
    • []
    • [ true, true, false, true, false, true, true ]
    • [ 3.14, 2, 3, 5, 7, 9, 11, 13, 17, 19, 23 ]
  • Objeto (object): é um conjunto de valores, semelhante à lista mas onde o índice possui um nome (nome da propriedade), que deve ser obrigatoriamente uma string. Um objeto é iniciado com o caracter “{” e finalizado com o caracter “}”. Os valores das propriedades podem ser de qualquer tipo, incluindo listas e outros objetos. Exemplos:
    • { “nome”: “Patolino” }
    • { “nome”: “Patolino”, “idade”: 22 }
    • { “estados_atendidos”: [ “DF”, “MT”, “MS”, “GO” ] }
    • { “pi”: 3.14, “raio”: 14938.4719, “altura”: 987 }
    • { “cadastro”: { “nome”: “Patolino”, “idade”: 12 } }
  • Tipo nulo – sem tipo e sem dados – null: quando uma propriedade não possui um tipo ele é tido como nulo e é representado pela palavra explicita null, sem aspas simples ou duplas. A presença de valores nulos é perigosa e deve ser evitada na declaração, ou mitigada no programa que recebe o dado nulo. Exemplos de JSON onde o null pode aparecer:
    • { “nome”: null, “idade”: null }
    • [ null, null, null, null ]
    • { “cadastro”: { “nome”: “Patolino”, “idade”: null } }

Com os tipos básicos, podemos preencher quaisquer variáveis usando JavaScript e algum dos valores acima.

O texto do JSON recebido ou enviado entre softwares deve ser um objeto: listas [] ou objetos {}. Quem vai determinar se o objeto é inicialmente object {} ou array [] será o sistema ou software que você irá usar para a interação.

Registro exportado para lista:

JSON
[
  1,
  "Patolino Pio",
  "2021-03-17",
  "M"
]

Registro exportado para objeto:

JSON
{
    "id": 1,
    "nome": "Patolino Pio",
    "nascimento": "2021-03-17",
    "sexo": "M"
}

Observe que objetos {} são melhores para conservar o nome da propriedade que armazena o valor. Listas são melhores para transportar vários objetos (tabelas, veja a seguir).

Veremos as implicações dos tipos e como acessar os dados mais adiante.

3 – Tabelas

Tabelas são transportadas em listas ou objetos, observe a tabela abaixo:

idnomenascimentosexo
1Patolino Pio2021-03-17M
2Pernalonga III2023-12-01M
3Felícia Feliz2022-07-25F
4Lilica Monte2024-07-09F

Representação JSON em lista (array) de elementos contendo registros (object):

JSON
[
  { "id": 1, "nome": "Patolino Pio", "nascimento": "2021-03-17", "sexo": "M" },
  { "id": 2, "nome": "Pernalonga III", "nascimento": "2023-12-01", "sexo": "M" },
  { "id": 3, "nome": "Felícia Feliz", "nascimento": "2022-07-25", "sexo": "F" },
  { "id": 4, "nome": "Lilica Monte", "nascimento": "2024-07-09", "sexo": "F" }
]

Apresentação JSON mais expandida do mesmo texto acima:

JSON
[
    {
        "id": 1,
        "nome": "Patolino Pio",
        "nascimento": "2021-03-17",
        "sexo": "M"
    },
    {
        "id": 2,
        "nome": "Pernalonga III",
        "nascimento": "2023-12-01",
        "sexo": "M"
    },
    {
        "id": 3,
        "nome": "Felícia Feliz",
        "nascimento": "2022-07-25",
        "sexo": "F"
    },
    {
        "id": 4,
        "nome": "Lilica Monte",
        "nascimento": "2024-07-09",
        "sexo": "F"
    }
]

Listas podem conter objetos, e esses objetos conter listas, assim o JSON pode transportar banco de dados inteiros. Exemplo de um banco de dados contendo 3 tabelas (clientes, login, emails) exportada em JSON:

JSON
{
    "clientes": [
       { "id": 1, "nome": "Francisco Silveira", "status": 1 },
       { "id": 2, "nome": "Amanda Beatriz", "status": 2 }
    ],
    "login": [
       { "cliente_id": 1, "user": "francisco", "senha": "tulipa" },
       { "cliente_id": 2, "nome": "amanda", "senha": "petunia" }
    ],
    "emails": [
       { "cliente_id": 1, "email": "francisco.sil@gmail.com", "use": true },
       { "cliente_id": 2, "email": "amanda2003@gmail.com", "use": true }
    ]
}

4 – Usando JSON em variáveis (JS)

O JSON tem como foco o acesso estruturado às informações, assim, saber o caminho de uma informação dentro de um documento JSON lhe permite acessar diretamente o que deseja. Vamos considerar uma variável “item” onde um JSON será armazenado:

JavaScript
let item = {
    "clientes": [
       { "id": 1, "nome": "Francisco Silveira", "status": 1 },
       { "id": 2, "nome": "Amanda Beatriz", "status": 2 }
    ],
    "usuarios": [
       { "cliente_id": 1, "user": "francisco", "senha": "tulipa" },
       { "cliente_id": 2, "nome": "amanda", "senha": "petunia" }
    ],
    "emails": [
       { "cliente_id": 1, "email": "francisco.sil@gmail.com", "use": true },
       { "cliente_id": 2, "email": "amanda2003@gmail.com", "use": true }
    ]
}

console.log(item.clientes)
// Retorno: array list
//   [
//      { "id": 1, "nome": "Francisco Silveira", "status": 1 },
//      { "id": 2, "nome": "Amanda Beatriz", "status": 2 }
//   ]

console.log(item.clientes[0])
// Retorno: object
//     { "id": 1, "nome": "Francisco Silveira", "status": 1 }

console.log(item.clientes[0].nome)
// Retorno: string
//     "Francisco Silveira"

5 – JSON transportando caracteres multibyte

Estamos acostumados a representar texto usando a tabela ASCII (A a Z, 0 a 9, !@#$%ˆ&*(){}’”;:/?.>,<~`|\) e isso basta para a maioria dos casos.

O JSON é naturalmente concebido para transportar caracteres multibyte em UTF-8, o que envolve o uso de alguns códigos binários dentro do texto – um detalhe a ser tratado com cuidado.

Observe o retorno de uma tradução chinês para português transportada em JSON:

JSON
{
  "input": "O rato roeu a roupa do rei de roma",
  "output": "老鼠咬破了罗马国王的衣服"
}

Todos os softwares devem ter capacidade de interpretar UTF-8, que é o padrão multibyte do formato JSON, assim, sempre que enviar ou receber um JSON usando HTTP, cuide para que o cabeçalho “Content-Type” seja definido com o valor “application/json; charset=utf-8” ou “application/json; charset=utf-16“, em vez do trivial “application/json“. Todos os 3 valore são corretos mas o “UTF-8” é o padrão RFC.

Infelizmente pode acontecer de um JSON ser processador por um software sem essas capacidades, exemplos e casos comuns:

  • Software travado em ISO: caracteres UTF serão convertidos de forma errada para ISO-8859 e corrompidos no processo, a palavra “comunicação” pode acabar se tornando “comunica��o“, nomes com acentos podem acabar corrompidos e gerando problemas na apresentação;
  • Softwares travando ao receber ou perceber conteúdo binário no texto;
  • Editores de texto antigos ou mal programados podem corromper o conteúdo do JSON (um caracter UTF-8 no final da string mal interpretado resulta na remoção das aspas que encerravam a string, corrompendo sintaticamente todo o JSON);

Quando for possível enfrentar algum dos problemas acima, deve-se optar por representar as strings em codificações seguras para transporte em ASCII (texto simples). Exemplos de codificação:

JSON
{
  "raw": "老鼠咬破了罗马国王的衣服",
  "utf8": "老鼠咬破了罗马国王的衣服",
  "unicodeEscape": "\u8001\u9f20\u54ac\u7834\u4e86\u7f85\u9a6c\u56fd\u738b\u7684\u8863\u670d",
  "urlEncoded": "%E8%80%81%E9%BC%A0%E5%92%AC%E7%A0%B4%E4%BA%86%E7%BD%97%E9%A9%AC%E5%9B%BD%E7%8E%8B%E7%9A%84%E8%A1%A3%E6%9C%8D",
  "base64": "6ICA6bKA5a2m5a6a5Y+v55Sf5Yqg5Y2B5bCG6K6k5L2/5a2m5a6a5Y+v"
}

O método base64 e unicodeEscape garantem que a string seja transportada no JSON e compatível com a tabela ASCII (formato universal). Embora não seja agradável esse tipo de gambiarra, ela pode servir para contornar problemas com unicode.

6 – JSON transportando binários

Quando se faz necessário transportar conteúdo 100% binário (bytes que vão variar entre o decimal 0 e 255, sem qualquer previsão de representação ASCII ou unicode), como é o caso de documentos (PDF, PPT, DOC, DOCX), imagens (JPEG/JPG, PNG, WEBP, GIF), áudio (WAV, MP3) e outros.

Não é possível colocar esse binário direto no JSON, ele obrigatoriamente precisará ser codificado. O base64 (RFC 4648) é o formato mais adequado para isso.

O texto base64 no mundo real pode ser grande e tornar o JSON enorme. Observe o exemplo abaixo onde crio um JSON com 3 documentos (anexos):

JSON
[
    {
        "filename": "cnh.pdf",
        "mime-type": "application/pdf",
        "content": "QmFzZSA2NCBkZSBleGVtcGxvIGFwZW5hcw=="
    },
    {
        "filename": "rg.pdf",
        "mime-type": "application/pdf",
        "content": "T3V0cm8gYmFzZTY0IGRlIGV4ZW1wbG8="
    },
    {
        "filename": "foto_rosto.png",
        "mime-type": "image/png",
        "content": "VGVyY2Vpcm8gYmFzZTY0IGRlIGV4ZW1wbG8="
    },
]

Usando os mesmos dados, observe um método mais simples (padrão de base64 incorporado em tag HTML):

JSON
[
  {
    "filename": "cnh.pdf",
    "content": "data:application/pdf;base64,QmFzZSA2NCBkZSBleGVtcGxvIGFwZW5hcw=="
  },
  {
    "filename": "rg.pdf",
    "content": "data:application/pdf;base64,T3V0cm8gYmFzZTY0IGRlIGV4ZW1wbG8="
  },
  {
    "filename": "foto_rosto.png",
    "content": "data:image/png;base64,VGVyY2Vpcm8gYmFzZTY0IGRlIGV4ZW1wbG8="
  }
]

7 – Bizarrices do mundo real

Observe este documento JSON:

JSON
{
    "tipo": "Cilindro",
    "nome": "Caixa-dagua-principal",
    "altura": "145",
    "largura": "125",
    "pi": "314",
    "instalado": "Não"
}

O programa que o produziu considera que o receptor irá processá-lo com as seguintes regras:

  • As propriedades “altura“, “largura” e “pi” são originalmente do tipo float, mas foram multiplicadas por 100 para serem convertidas em inteiro, e depois foram representadas no JSON como string;
  • O receptor deve converter essas 3 propriedades acima para inteiro, depois para float dividindo por 100 para chegar ao número de ponto flutuante exato;
  • A propriedade “instalado“, quando for “Não”, “Nao”, “nao”, “NÃO”, “No”, “NO” ele deverá ser convertido em false, caso contrario ele será considerado true;

Você se pergunta: “Por que alguem faria desse jeito?”. Não seria mais simples assim:

JSON
{
    "tipo": "Cilindro",
    "nome": "Caixa-dagua-principal",
    "altura": 1.45,
    "largura": 1.25,
    "pi": 3.14,
    "instalado": false
}

Obviamente o formato acima seria melhor e dentro das melhores práticas. Infelizmente exemplos assim são comuns e você deve ficar sempre atendo aos padrões do emissor e receptor dos dados para não enlouquecer.

Muitos programadores se cansam dos problemas causados ao lidar com o armazenamento binário dos pontos flutuantes e preferem usar inteiros, exemplo:

  • Para dinheiro, armazenar a quantidade de centavos, usando 50 em vez de 0.5 (OpenPIX é um site que faz cobranças usando valores assim);
  • Em um posto de gasolina, para cobrar por mililitros, 1 litro seria representado por 1000 ml, e multiplica pelo valor do ml em vez do valor do litro, arredondando para cima (você perde frações de centavo para o posto);
  • Usar palavras “Ativado” ou “Desativado” em vez de true ou false pode ser uma forma preguiçosa de não converter os dados e apenas mostrar ao usuário na tela;

Fique atento e boa sorte.

Conclusão

JSON veio para ficar. Você deve possuir destreza e domínio completo dele para se destacar nesse novo mundo automatizado, integrado e guiado por Inteligência Artificial.

Espero que tenha ajudado, até mais!

Patrick Brandão, patrickbrandao@gmail.com