Formatação, validação e JSON Schema na prática

Publicado em 2026-04-13 8 min de leitura

Resumo (TL;DR)

Um time com o qual trabalhei mês passado enviou uma edição de uma linha para feature_flags.json. O JSON.parse aceitou, o CI ficou verde, e quando chegou em staging todo ramo de checkout caiu no caminho de código legado porque flags.checkout_v2 era a string "true" em vez do boolean true. “Checar o JSON” tinha virado silenciosamente três atividades diferentes, e eles só haviam realizado as duas primeiras. Pretty-print reformata os espaços em branco para um humano ler; não verifica nada. Validação de sintaxe confirma que uma string é parseável como JSON segundo a RFC 8259 — colchetes casados, aspas corretas, literais numéricos válidos — e isso é exatamente o que o JSON.parse faz. Validação estrutural é uma etapa separada que pergunta se o valor parseado tem a forma que seu programa espera: campos obrigatórios, tipos corretos, valores de enum permitidos, comprimentos de string, faixas numéricas. Essa última etapa é justamente para o que o JSON Schema foi desenhado, e é a que a maioria dos times pula até que um bug em produção como o de cima force a adicionar. Um pipeline confiável usa as três na camada certa: formate quando precisar ler os dados, parseie para pegar entrada malformada e rode uma checagem de schema no estilo Ajv em fronteiras de confiança — requisições de API entrando, arquivos de configuração e mensagens entre serviços.

Contexto e conceitos

JSON é definido pela RFC 8259 (e pela equivalente ECMA-404). A gramática é pequena de propósito. Um documento JSON é um de: string, número, true, false, null, array ou objeto. Strings usam aspas duplas e suportam uma lista curta de escapes incluindo \n, \t, \", \\ e \uXXXX para unidades de código Unicode. Números seguem uma gramática decimal com sinal opcional, parte fracionária e expoente, mas não há distinção entre inteiro e ponto flutuante no nível de sintaxe. Objetos são coleções não ordenadas de membros com chaves string, arrays são listas ordenadas, e espaços em branco fora de strings são insignificantes.

O que o JSON intencionalmente não inclui é quase tão importante. Não há comentários, não há vírgulas finais, não há strings com aspas simples, nem literais hexadecimais ou binários. Caracteres Unicode fora do ASCII precisam aparecer ou como bytes UTF-8 brutos no fluxo codificado ou como escapes \u. Extensões como JSON5 e HJSON relaxam algumas dessas regras, mas são formatos separados, e parsers que aceitam JSON estrito vão rejeitá-las.

Uma vez que um documento é parseado, sua validade sintática não diz nada sobre se é o dado certo. Um payload de login como {"user": "x", "pass": "y"} é JSON perfeitamente válido, mesmo que seu endpoint esperasse {"username": "...", "password": "..."}. Para pegar essa classe de erro, você precisa de um schema: uma descrição legível por máquina do que conta como documento aceitável. O JSON Schema (o meta-schema recomendado atualmente é o Draft 2020-12) preenche essa lacuna. Ele suporta campos obrigatórios, restrições de tipo, enum e const, padrões de string via regex, faixas numéricas, items e unicidade em arrays, properties e additionalProperties de objetos, e composição via allOf, oneOf, anyOf e $ref. O Ajv 8.12 compila um schema uma vez em uma função validadora JavaScript, o que é rápido o bastante para caminhos quentes — em um serviço Node 20.11 que instrumentei, uma checagem Ajv compilada sobre um corpo típico de requisição ficou na faixa das dezenas de microssegundos.

Pretty-print é o mais comum dos três. Ele apenas insere espaços em branco — indentação e quebras de linha — sem mudar o significado. A maioria dos editores e ferramentas de linha de comando faz isso; o devtools do navegador faz automaticamente no painel de rede. É útil para humanos e irrelevante para máquinas.

Comparação e dados

AspectoPretty-printValidação de sintaxeValidação por JSON Schema
PropósitoDeixar o dado legívelGarantir que o texto parseia como JSONGarantir que o dado parseado bate com a forma esperada
DetectaNada — só espaçosColchetes não casados, aspas erradas, escapes inválidos, vírgulas finaisCampos faltantes, tipos errados, valores fora de faixa, chaves desconhecidas
Não detectaProblemas estruturais, semânticosProblemas estruturais (forma esperada), regras de negócioRegras semânticas fora do schema, invariantes entre campos sem palavras-chave customizadas
Ferramental típicoJSON.stringify(obj, null, 2), jq, formatador do IDEJSON.parse, jq -e, qualquer parserAjv 8.x, python-jsonschema, validadores OpenAPI
Onde rodarFerramentas de dev, logsEm toda fronteira de parse (implícito)Em requisição/resposta de API, carga de config, fronteiras de mensagem

As três colunas não são alternativas; são camadas sequenciais. Abrir um package-lock.json de 12 MB em uma única linha em um editor cru é como você acaba com uma string que sua ferramenta de diff se recusa a comparar, e formatá-la na forma indentada não valida nada — só torna a forma legível. Essa distinção importa porque as duas camadas seguintes, sintaxe e schema, costumam ser confundidas com “eu formatei e nada explodiu.” Pretty-print para ler, parse para pegar texto malformado, e validação com schema para pegar forma errada. Enviar só os dois primeiros é uma lacuna comum.

Cenários reais

Cenário 1 — Depurar uma resposta de API. Um gateway de pagamento de terceiros retorna um corpo JSON de 600 linhas e o navegador mostra como linha única. Fazer pretty-print — seja com devtools do navegador, um formatador local ou curl ... | jq . — transforma em algo que um humano consegue ler. Nenhuma validação acontece aqui; o objetivo é legibilidade enquanto você procura o campo que parece errado, e a disciplina importante é não chamar essa etapa de “validado.”

Cenário 2 — Carregar um arquivo de configuração. Um serviço lê config.json na inicialização. Um parser JSON estrito pega erros de sintaxe como uma vírgula final perdida e recusa subir, o que é o comportamento correto. Mas, como o incidente de abertura mostrou, um arquivo válido com retries: "three" em vez de retries: 3 vai parsear direitinho e só falhar quando o código tentar comparar uma string com um número. O padrão que funcionou para mim é colocar Ajv.compile(schema)(config) nas primeiras linhas do ponto de entrada e chamar process.exit(1) em caso de falha — uma mudança de cinco minutos que se pagou das duas próximas vezes em que alguém editou o arquivo à mão.

Cenário 3 — Verificar um contrato OpenAPI. Um time envia um documento OpenAPI 3.1 que descreve endpoints, corpos de requisição e formas de resposta em components.schemas. Testes de contrato pegam os payloads de exemplo da spec e validam contra os schemas usando um validador JSON Schema. Quando uma implementação de servidor derrapa — digamos, começa a retornar um inteiro onde a spec prometia uma string — o teste de contrato sinaliza a incompatibilidade antes que um cliente quebre em produção. É o mesmo motor JSON Schema que você usaria para um arquivo de config solitário; a diferença é a escala de cobertura.

Equívocos comuns

JSON.parse é suficiente para validar JSON.” Ele valida sintaxe, não forma. Um parser vai alegremente retornar um objeto sem metade dos campos que seu código precisa. Trate JSON.parse como um portão contra texto malformado e ponha uma camada de schema em cima quando o dado atravessa uma fronteira de confiança.

“JSON Schema é só do lado do servidor.” Validar no navegador antes de enviar uma requisição dá feedback instantâneo ao usuário e corta carga do servidor. Muitas bibliotecas de formulário e o próprio Ajv rodam confortavelmente no navegador. A validação server-side ainda precisa rodar — nunca confie no cliente — mas checagens no cliente melhoram UX sem enfraquecer o modelo de segurança.

“JSON5 é apenas JSON com comentários.” JSON5 adiciona comentários, chaves sem aspas, vírgulas finais, números hexadecimais e mais. Isso o torna mais amigável como formato de config editado por humanos — o consumidor mais conhecido é o tsconfig.json, que na verdade usa JSONC (um superset não-padrão diferente) — mas qualquer coisa que siga estritamente a RFC 8259 não aceitará. Use JSON5/JSONC onde o consumidor documenta suporte; emita JSON estrito ao escrever para a rede ou para qualquer ferramenta cujo parser você não controla.

“YAML é só JSON com indentação.” Todo documento JSON válido é YAML válido, mas YAML adiciona recursos — âncoras e aliases, tipos com tag, múltiplos documentos em um arquivo, escalares em bloco e dobrados, parsing sensível à indentação — que introduzem bugs que o JSON não tem. O clássico é o chamado “problema da Noruega”: YAML 1.1 interpreta NO como o boolean false a menos que você coloque entre aspas. Migrar de JSON para YAML “para ficar legível” troca uma classe de problemas por outra.

Checklist

  1. Você só precisa ler o dado? Faça pretty-print. Não diga que está “validado.”
  2. Ele está cruzando uma fronteira de confiança (requisição HTTP, fila de mensagens, arquivo de config)? Parseie e depois rode uma checagem JSON Schema, não apenas o parse.
  3. Você se importa com campos desconhecidos? Defina additionalProperties: false no schema e decida se rejeita ou remove extras.
  4. Os erros são acionáveis? Configure o validador (por exemplo, Ajv com allErrors: true) para retornar toda violação e o usuário ver todos os problemas de uma vez.
  5. O schema vive perto do código que usa o dado? Drift entre uma spec e uma implementação é mais fácil de prevenir quando o schema é fonte de verdade para ambos.
  6. O formato é estável o bastante para JSON? Se humanos precisam editar e você controla o parser, JSON5/JSONC ou TOML podem ser mais tolerantes. Se máquinas trocam o dado, fique com JSON estrito.

Ferramenta relacionada

O formatador JSON da Patrache Studio roda no navegador, então o payload que você cola nunca sai da sua máquina — útil quando você inspeciona uma resposta de produção que contém dados pessoais. Payloads raramente vivem sozinhos: se o JSON que você formata inclui um binário embutido, as regras em Base64 e codificação de URL: propósito, armadilhas e uso correto explicam por que o blob expande e quando um transporte diferente é apropriado. Se o payload inclui IDs, UUID v1 vs v4 vs v7: escolhendo uma chave primária de BD cobre por que a versão exata que você gera no servidor afeta a forma como sistemas a jusante indexam, ordenam e cacheiam esse JSON.

Referências