Ir para o conteúdo
Toova
Todas as Ferramentas

Como Formatar JSON em JavaScript — 5 Métodos

Toova

JSON está em todo lugar: respostas de API, arquivos de configuração, exportações de banco de dados, pipelines de log. Mas a saída JSON bruta frequentemente é um blob compacto — sem quebras de linha, sem recuo, chaves em qualquer ordem que o serializador produzir. Isso torna a depuração dolorosa e a colaboração mais difícil do que precisa ser.

O JavaScript oferece várias formas de formatar JSON, desde o JSON.stringify nativo até bibliotecas de streaming para arquivos grandes demais para carregar na memória. Este guia percorre cinco métodos práticos, cobrindo os casos mais comuns e os casos extremos que pegam os desenvolvedores de surpresa.

Método 1: JSON.stringify com Recuo

A abordagem mais simples já está integrada na linguagem. O JSON.stringify aceita três argumentos: o valor a serializar, um replacer (mais sobre isso no Método 2) e um parâmetro space que controla o recuo.

const data = {
  name: "Alice",
  role: "engineer",
  skills: ["JavaScript", "TypeScript", "Node.js"],
  active: true,
};

// Recuo de 2 espaços (padrão comum)
console.log(JSON.stringify(data, null, 2));

// Recuo de 4 espaços
console.log(JSON.stringify(data, null, 4));

// Recuo com tabulação
console.log(JSON.stringify(data, null, "\t"));

Com null, 2, a saída fica assim:

{
  "name": "Alice",
  "role": "engineer",
  "skills": [
    "JavaScript",
    "TypeScript",
    "Node.js"
  ],
  "active": true
}

Dois espaços é a convenção mais comum em projetos JavaScript — corresponde ao padrão no ESLint, Prettier e na maioria dos guias de estilo. Quatro espaços é comum em ferramentas adjacentes ao Python. Quando space é omitido ou definido como 0, a saída é uma string compacta de uma linha — ideal para transmissão em rede.

Você também pode formatar JSON instantaneamente no browser usando a ferramenta JSON Formatter — cole o JSON bruto, obtenha saída formatada e copie o resultado sem escrever nenhum código.

Método 2: Função Replacer Personalizada

O segundo argumento do JSON.stringify — o replacer — é uma função ou array que filtra e transforma valores antes da serialização. É aqui que você tem controle real sobre o que acaba na saída.

A forma de array é a abordagem mais simples para incluir apenas chaves específicas:

const user = {
  id: 42,
  name: "Bob",
  password: "s3cr3t",
  email: "bob@example.com",
  createdAt: new Date(),
};

// Replacer de array: inclui apenas as chaves listadas
const safeJson = JSON.stringify(user, ["id", "name", "email"], 2);
console.log(safeJson);
// {
//   "id": 42,
//   "name": "Bob",
//   "email": "bob@example.com"
// }

A forma de função dá controle por valor. Retorne o valor para incluí-lo, retorne undefined para descartá-lo, ou retorne um valor transformado:

function replacer(key, value) {
  // Descarta chaves que começam com underscore (convenção privada)
  if (key.startsWith("_")) return undefined;

  // Mascara campos sensíveis
  if (key === "password" || key === "token") return "[REDACTED]";

  // Converte objetos Date para strings ISO explicitamente
  if (value instanceof Date) return value.toISOString();

  return value;
}

const payload = {
  id: 1,
  name: "Carol",
  password: "hunter2",
  _internalFlag: true,
  lastLogin: new Date("2026-05-01"),
};

console.log(JSON.stringify(payload, replacer, 2));
// {
//   "id": 1,
//   "name": "Carol",
//   "password": "[REDACTED]",
//   "lastLogin": "2026-05-01T00:00:00.000Z"
// }

O replacer é executado recursivamente em cada objeto e array aninhado. Esse padrão é útil para pipelines de log onde você quer remover credenciais antes de escrever no disco ou enviar para um serviço de observabilidade.

Método 3: Formatação com Chaves Ordenadas

Objetos JavaScript não garantem a ordem das chaves (embora o V8 e a maioria dos engines preservem a ordem de inserção para chaves string). Quando você precisa de saída determinística — para diff, cache ou representações canônicas — ordenar as chaves alfabeticamente é a decisão certa.

function sortedStringify(value, indent = 2) {
  return JSON.stringify(value, sortReplacer, indent);
}

function sortReplacer(key, value) {
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
    return Object.keys(value)
      .sort()
      .reduce((sorted, k) => {
        sorted[k] = value[k];
        return sorted;
      }, {});
  }
  return value;
}

const config = {
  version: "1.0",
  author: "Dave",
  dependencies: { typescript: "^5.4", eslint: "^9.0", astro: "^5.0" },
  name: "my-project",
};

console.log(sortedStringify(config));
// {
//   "author": "Dave",
//   "dependencies": { "astro": "^5.0", "eslint": "^9.0", "typescript": "^5.4" },
//   "name": "my-project",
//   "version": "1.0"
// }

Chaves ordenadas garantem que diffs do git mostrem apenas as linhas que realmente mudaram, em vez de reordenações arbitrárias de diferentes serializadores. Isso é particularmente útil para o package.json e arquivos de configuração similares versionados no controle de versão.

Para converter JSON para outros formatos, a ferramenta JSON para YAML e a JSON para CSV também lidam com a ordenação de chaves na saída.

Método 4: Formatar a Partir de uma String (Parse + Stringify)

O JSON do mundo real geralmente chega como uma string — de uma resposta fetch, uma leitura de arquivo, uma colagem de área de transferência ou uma coluna TEXT de banco de dados. Você precisa fazer o parse primeiro e depois reformatá-lo. A parte crítica é o tratamento adequado de erros: JSON inválido lançará uma exceção, e você quer capturá-la adequadamente.

function formatJsonString(rawString, indent = 2) {
  try {
    const parsed = JSON.parse(rawString);
    return { ok: true, result: JSON.stringify(parsed, null, indent) };
  } catch (err) {
    return { ok: false, error: err.message };
  }
}

const raw = '{"name":"Eve","scores":[100,95,88],"active":true}';
const { ok, result, error } = formatJsonString(raw);

if (ok) {
  console.log(result);
  // {
  //   "name": "Eve",
  //   "scores": [100, 95, 88],
  //   "active": true
  // }
} else {
  console.error("Falha no parsing:", error);
}

// Entrada inválida
const bad = '{"name": "Eve", "broken":}';
const r2 = formatJsonString(bad);
// { ok: false, error: "Unexpected token '}'" }

Encapsular erros de parse em um objeto de retorno estruturado torna essa função segura para usar em componentes de UI e scripts de build sem envolver cada chamada em um try/catch. A documentação MDN do JSON.stringify cobre a especificação completa dos parâmetros, e a RFC 8259 define como é um JSON válido no nível do protocolo.

Método 5: Streaming para Arquivos Grandes

Os métodos 1 a 4 carregam toda a estrutura JSON na memória antes de formatar. Para arquivos de centenas de megabytes ou vários gigabytes, isso bloqueia o event loop do Node.js e pode travar o processo completamente.

A abordagem de streaming lê o arquivo em chunks e escreve a saída formatada de forma incremental. Para NDJSON (um objeto JSON por linha, comum em arquivos de log e exportações de banco de dados), uma abordagem baseada em readline funciona sem dependências extras:

import { createReadStream, createWriteStream } from "node:fs";
import { createInterface } from "node:readline";

async function formatNdjsonFile(inputPath, outputPath) {
  const rl = createInterface({
    input: createReadStream(inputPath),
    crlfDelay: Infinity,
  });

  const output = createWriteStream(outputPath);

  for await (const line of rl) {
    if (!line.trim()) continue;
    try {
      const obj = JSON.parse(line);
      output.write(JSON.stringify(obj, null, 2) + "\n---\n");
    } catch (e) {
      output.write("[Linha JSON inválida: " + e.message + "]\n---\n");
    }
  }

  output.end();
}

NDJSON é o formato de streaming mais simples: cada linha é um objeto JSON completo e válido. Muitas ferramentas de exportação o suportam precisamente porque é trivialmente transmissível em stream. Se você controla o formato de grandes exportações de dados, prefira NDJSON em vez de um único array JSON gigante.

Casos Extremos para Ficar Atento

Esses são os cenários onde a formatação JSON padrão falha silenciosamente ou lança exceções de forma inesperada.

Referências Circulares

O JSON.stringify lança um TypeError se um objeto faz referência a si mesmo direta ou indiretamente. Corrija isso com um replacer que rastreia objetos visitados usando um WeakSet:

function safeStringify(obj, indent = 2) {
  const seen = new WeakSet();
  return JSON.stringify(obj, (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) return "[Circular]";
      seen.add(value);
    }
    return value;
  }, indent);
}

const a = { name: "circular" };
a.self = a;
console.log(safeStringify(a));
// { "name": "circular", "self": "[Circular]" }

O WeakSet mantém referências sem impedir a coleta de lixo, o que evita vazamentos de memória em processos de longa duração.

Valores BigInt

O JSON.stringify lança um TypeError para valores BigInt porque a especificação JSON não tem tipo inteiro de 64 bits. Converta para string no seu replacer:

const data = { id: 9007199254740993n, value: 42 };

JSON.stringify(data, (key, value) =>
  typeof value === "bigint" ? value.toString() : value
, 2);
// { "id": "9007199254740993", "value": 42 }

Valores Map e Set

Map é serializado como um objeto vazio e Set como um array vazio — não com seus conteúdos. Converta-os explicitamente em um replacer:

const data = {
  tags: new Set(["json", "javascript"]),
  meta: new Map([["source", "api"]]),
};

JSON.stringify(data, (key, value) => {
  if (value instanceof Set) return [...value];
  if (value instanceof Map) return Object.fromEntries(value);
  return value;
}, 2);
// { "tags": ["json", "javascript"], "meta": { "source": "api" } }

Valores undefined

Propriedades de objeto com valores undefined são descartadas silenciosamente. Posições de array com undefined se tornam null. Use um replacer para converter undefined em null quando precisar preservar todas as chaves:

const obj = { a: 1, b: undefined, c: null };
JSON.stringify(obj, null, 2);
// { "a": 1, "c": null }  — "b" é descartado silenciosamente

// Correção:
JSON.stringify(obj, (key, value) =>
  value === undefined ? null : value
, 2);
// { "a": 1, "b": null, "c": null }

Formate JSON Instantaneamente no Browser

Se você precisa formatar um blob JSON agora sem escrever nenhum código, o Toova JSON Formatter faz isso com um clique — cole o JSON bruto, obtenha saída formatada com recuo de 2 ou 4 espaços e copie o resultado. Sem cadastro, sem upload de arquivo, tudo roda localmente no seu browser.

Para conversões entre formatos, JSON para YAML e JSON para CSV seguem a mesma abordagem de privacidade em primeiro lugar — seus dados nunca saem do seu dispositivo.

Conclusão

Para a maioria dos casos de uso, JSON.stringify(obj, null, 2) é tudo que você precisa. Adicione uma função replacer quando precisar de filtragem, mascaramento ou chaves ordenadas. Envolva o JSON.parse em um try/catch ao lidar com entrada externa. Recorra ao streaming apenas quando o tamanho do arquivo torna o parsing síncrono impraticável. E mantenha os casos extremos — referências circulares, BigInt, Map/Set, undefined — no fundo da mente ao trabalhar com formatos de dados incomuns.