Como formatar e validar JSON em JavaScript, Python e PHP

Formatar JSON é ler o texto e escrevê-lo de novo com quebras de linha e recuo. Validar é tentar ler e tratar o erro. Este guia mostra as duas coisas em JavaScript, Python e PHP, lista os erros que mais quebram um JSON e explica as diferenças que pegam de surpresa, como acentos escapados no PHP e NaN aceito no Python.

Por Leonardo Gaertner · Publicado em

Os erros que mais quebram um JSON

  • Vírgula sobrando depois do último item: {"a": 1,}.
  • Aspas simples em vez de duplas: {'a': 1}. O JSON só aceita aspas duplas.
  • Chaves sem aspas: {a: 1}. Isso é objeto de JavaScript, não JSON.
  • Valores que o JSON não tem: undefined, NaN, Infinity, comentários e datas sem aspas.

O validador de JSON aponta a linha e a coluna do erro, e o formatador de JSON deixa o texto legível. Em código, o caminho é o mesmo: ler, tratar o erro e escrever de novo.

Em JavaScript

O terceiro argumento de JSON.stringify define o recuo. A mensagem de erro do JSON.parse muda entre navegadores e versões do Node.js, então use-a para mostrar ao usuário, não para tomar decisões no código.

JavaScript
function formatarJSON(texto, espacos = 2) {
  return JSON.stringify(JSON.parse(texto), null, espacos);
}

function validarJSON(texto) {
  try {
    JSON.parse(texto);
    return { valido: true };
  } catch (erro) {
    return { valido: false, mensagem: erro.message };
  }
}

Em Python

O json.loads aceita NaN e Infinity, que não são JSON: o parse_constant recusa esses valores. Sem ensure_ascii=False, o json.dumps escreve \u00e7 no lugar de ç. O erro traz a linha e a coluna.

Python
import json


def _recusar(nome):
    raise ValueError(f"{nome} não é JSON válido")


def formatar_json(texto: str, espacos: int = 2) -> str:
    # parse_constant: o Python aceitaria NaN e Infinity, que o JSON não permite
    dados = json.loads(texto, parse_constant=_recusar)
    return json.dumps(dados, indent=espacos, ensure_ascii=False)


def validar_json(texto: str) -> dict:
    try:
        json.loads(texto, parse_constant=_recusar)
        return {"valido": True}
    except json.JSONDecodeError as e:
        return {"valido": False, "mensagem": e.msg, "linha": e.lineno, "coluna": e.colno}
    except ValueError as e:
        return {"valido": False, "mensagem": str(e)}

Em PHP

Com JSON_THROW_ON_ERROR, o json_decode lança uma exceção em vez de devolver null em silêncio, o que evita confundir um JSON inválido com o valor null. O JSON_PRETTY_PRINT usa quatro espaços. No PHP 8.3 ou mais novo, json_validate confere sem montar os dados.

PHP
<?php
function formatarJson(string $texto): string
{
    $dados = json_decode($texto, false, 512, JSON_THROW_ON_ERROR);
    // Sem as duas últimas opções, o PHP escreveria \u00e7 no lugar de ç
    // e \/ no lugar de /
    return json_encode(
        $dados,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
}

function validarJson(string $texto): array
{
    try {
        json_decode($texto, false, 512, JSON_THROW_ON_ERROR);
        return ['valido' => true];
    } catch (JsonException $e) {
        return ['valido' => false, 'mensagem' => $e->getMessage()];
    }
}

Diferenças entre as linguagens

  • Números: o JavaScript escreve 1.0 como 1, e o Python mantém 1.0. Os dois são o mesmo número em JSON.
  • Números grandes: no JavaScript, inteiros acima de 9007199254740991 perdem precisão no JSON.parse. IDs grandes devem trafegar como texto.
  • Objeto vazio no PHP: com json_decode($texto, true), o {} vira array vazio e volta como []. Por isso o exemplo usa objetos (false).
  • Ordem das chaves: as três linguagens mantêm a ordem do texto, mas o JSON não garante ordem. Não dependa dela.

Achou um erro neste guia? Avise por e-mail.

Perguntas frequentes

Como formatar JSON em JavaScript?

Use JSON.stringify(JSON.parse(texto), null, 2). O último argumento é o número de espaços do recuo. Se o texto não for JSON válido, o JSON.parse lança um erro.

JSON aceita comentários?

Não. Comentários, vírgula sobrando e aspas simples tornam o texto inválido. Alguns formatos derivados, como o JSONC usado em arquivos de configuração, aceitam comentários, mas não são JSON puro.

Por que o PHP escreve \u00e7 no lugar de ç no JSON?

Por padrão, o json_encode escapa caracteres fora do ASCII. A opção JSON_UNESCAPED_UNICODE mantém os acentos. O resultado é equivalente, mas fica mais legível e menor.

Como achar o erro em um JSON grande?

Use uma ferramenta que mostre a linha e a coluna, como o validador de JSON do PraDev, ou o json.loads do Python, que traz essas informações no erro.