JSON: parse, stringify e boas práticas
1. Introdução ao JSON no ecossistema JavaScript
JSON (JavaScript Object Notation) é o formato de intercâmbio de dados mais utilizado no desenvolvimento web moderno. Sua simplicidade e compatibilidade com JavaScript o tornaram o padrão de fato para APIs REST, arquivos de configuração e armazenamento local.
Embora JSON se pareça com objetos JavaScript, existem diferenças fundamentais:
- Aspas duplas obrigatórias: todas as chaves e strings devem usar aspas duplas
- Tipos suportados: apenas strings, números, booleanos, null, arrays e objetos
- Valores não suportados: undefined, funções, símbolos, Map, Set, Date (em formato nativo)
// Objeto JavaScript válido
const user = {
name: 'João',
age: 30,
sayHello: () => console.log('Olá'),
id: undefined
};
// JSON equivalente (inválido como objeto JS sem aspas nas chaves)
const userJSON = '{"name": "João", "age": 30}';
Casos de uso típicos incluem:
- Comunicação com APIs REST
- Arquivos de configuração (package.json, tsconfig.json)
- Armazenamento no localStorage/sessionStorage
- Serialização de dados para transmissão em rede
2. JSON.parse(): convertendo strings em dados utilizáveis
O método JSON.parse() converte uma string JSON em um objeto JavaScript. Sua sintaxe aceita dois parâmetros:
JSON.parse(text[, reviver])
Tratamento de erros com try/catch
Sempre envolva chamadas a JSON.parse() em blocos try/catch para evitar crashes com JSON inválido:
const dadosInseguros = '{"nome": "Maria", "idade": "25"}';
try {
const usuario = JSON.parse(dadosInseguros);
console.log(usuario.nome); // "Maria"
} catch (erro) {
console.error('Falha ao parsear JSON:', erro.message);
}
Usando o parâmetro reviver
O parâmetro reviver permite transformar valores durante a conversão. É útil para converter strings de data em objetos Date:
const jsonComData = '{"nome": "Ana", "nascimento": "1990-05-15T10:30:00.000Z"}';
const pessoa = JSON.parse(jsonComData, (chave, valor) => {
if (chave === 'nascimento') {
return new Date(valor);
}
return valor;
});
console.log(pessoa.nascimento instanceof Date); // true
console.log(pessoa.nascimento.getFullYear()); // 1990
3. JSON.stringify(): serializando objetos para transmissão
JSON.stringify() converte um objeto JavaScript em uma string JSON. Aceita três parâmetros:
JSON.stringify(value[, replacer[, space]])
Controle com replacer
O parâmetro replacer pode ser uma função ou array para filtrar quais propriedades incluir:
const usuario = {
id: 1,
nome: 'Carlos',
senha: '123456',
email: 'carlos@email.com'
};
// Usando array para selecionar campos
const jsonSeguro = JSON.stringify(usuario, ['nome', 'email']);
console.log(jsonSeguro); // {"nome":"Carlos","email":"carlos@email.com"}
// Usando função para transformar dados
const jsonFormatado = JSON.stringify(usuario, (chave, valor) => {
if (chave === 'senha') return undefined; // Remove campo sensível
return valor;
});
Formatação com espaço
O parâmetro space adiciona indentação para tornar o JSON legível:
const dados = { nome: 'João', endereco: { cidade: 'SP', uf: 'SP' } };
console.log(JSON.stringify(dados, null, 2));
// {
// "nome": "João",
// "endereco": {
// "cidade": "SP",
// "uf": "SP"
// }
// }
4. Boas práticas de segurança com JSON
Nunca use eval() para parsear JSON
eval() executa código arbitrário e pode levar a ataques de injeção:
// ❌ PERIGOSO - nunca faça isso
const dadosMaliciosos = 'console.log("Código executado!")';
eval('(' + dadosMaliciosos + ')'); // Executa código malicioso
// ✅ Correto - use JSON.parse
try {
const dados = JSON.parse(dadosMaliciosos);
} catch (e) {
console.error('JSON inválido');
}
Validar dados de fontes externas
Sempre valide dados recebidos de APIs ou formulários. Bibliotecas como Zod ou Joi ajudam:
import { z } from 'zod';
const schemaUsuario = z.object({
nome: z.string().min(2).max(100),
email: z.string().email(),
idade: z.number().int().positive()
});
function processarDados(jsonString) {
try {
const dadosBrutos = JSON.parse(jsonString);
const dadosValidados = schemaUsuario.parse(dadosBrutos);
return dadosValidados;
} catch (erro) {
console.error('Dados inválidos:', erro.errors);
return null;
}
}
Cuidados com prototype pollution
Ao mesclar objetos, evite que chaves como __proto__ contaminem o protótipo:
// ❌ Perigoso - pode causar prototype pollution
function mergePerigoso(alvo, fonte) {
for (const chave in fonte) {
alvo[chave] = fonte[chave];
}
}
// ✅ Seguro - use Object.assign ou spread operator
function mergeSeguro(alvo, fonte) {
return { ...alvo, ...fonte };
}
// Ou verifique explicitamente
function mergeComValidacao(alvo, fonte) {
for (const [chave, valor] of Object.entries(fonte)) {
if (chave !== '__proto__' && chave !== 'constructor') {
alvo[chave] = valor;
}
}
return alvo;
}
5. Manipulação de dados complexos e edge cases
Serializando Date, RegExp, Map e Set
Tipos especiais não são serializados automaticamente:
const dadosComplexos = {
data: new Date('2024-01-15'),
regex: /[a-z]+/g,
mapa: new Map([['chave', 'valor']]),
conjunto: new Set([1, 2, 3])
};
// ❌ Comportamento padrão - perde informações
console.log(JSON.stringify(dadosComplexos));
// {"data":"2024-01-15T00:00:00.000Z","regex":{},"mapa":{},"conjunto":{}}
// ✅ Solução: implementar toJSON() ou usar replacer
dadosComplexos.mapa.toJSON = function() {
return Object.fromEntries(this);
};
dadosComplexos.conjunto.toJSON = function() {
return [...this];
};
console.log(JSON.stringify(dadosComplexos));
// {"data":"2024-01-15T00:00:00.000Z","regex":{},"mapa":{"chave":"valor"},"conjunto":[1,2,3]}
Lidando com referências circulares
Objetos com referências circulares causam erro:
const objA = { nome: 'A' };
const objB = { nome: 'B', ref: objA };
objA.ref = objB;
// ❌ TypeError: Converting circular structure to JSON
// JSON.stringify(objA);
// ✅ Solução: detectar e substituir referências
function stringifyComCirculares(obj) {
const vistos = new WeakSet();
return JSON.stringify(obj, (chave, valor) => {
if (typeof valor === 'object' && valor !== null) {
if (vistos.has(valor)) return '[Circular]';
vistos.add(valor);
}
return valor;
});
}
console.log(stringifyComCirculares(objA));
// {"nome":"A","ref":{"nome":"B","ref":"[Circular]"}}
Tratamento de valores especiais
const valoresEspeciais = {
indefinido: undefined, // Será removido
nulo: null, // Mantido como null
nan: NaN, // Convertido para null
infinito: Infinity // Convertido para null
};
console.log(JSON.stringify(valoresEspeciais));
// {"nulo":null,"nan":null,"infinito":null}
6. JSON no Node.js: leitura e escrita de arquivos
Lendo arquivos JSON
import fs from 'fs';
import path from 'path';
function lerArquivoJSON(caminho) {
try {
const dados = fs.readFileSync(path.resolve(caminho), 'utf-8');
return JSON.parse(dados);
} catch (erro) {
if (erro.code === 'ENOENT') {
console.error('Arquivo não encontrado:', caminho);
return null;
}
console.error('Erro ao ler JSON:', erro.message);
return null;
}
}
const config = lerArquivoJSON('./config.json');
Escrevendo arquivos JSON
function salvarArquivoJSON(caminho, dados) {
try {
const jsonString = JSON.stringify(dados, null, 2);
fs.writeFileSync(path.resolve(caminho), jsonString, 'utf-8');
console.log('Arquivo salvo com sucesso');
} catch (erro) {
console.error('Erro ao salvar JSON:', erro.message);
}
}
const usuario = {
id: 1,
nome: 'Maria',
email: 'maria@email.com',
createdAt: new Date().toISOString()
};
salvarArquivoJSON('./usuarios/1.json', usuario);
Validação com schema antes de salvar
import { z } from 'zod';
import fs from 'fs';
const schemaConfig = z.object({
port: z.number().int().min(1024).max(65535),
host: z.string().ip(),
debug: z.boolean().optional()
});
function salvarConfigSegura(caminho, dados) {
try {
const validados = schemaConfig.parse(dados);
fs.writeFileSync(caminho, JSON.stringify(validados, null, 2));
return true;
} catch (erro) {
console.error('Configuração inválida:', erro.errors);
return false;
}
}
7. JSON no React: estado, props e armazenamento local
Parseando dados de APIs no useEffect
import { useState, useEffect } from 'react';
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [erro, setErro] = useState(null);
useEffect(() => {
async function carregarDados() {
try {
const resposta = await fetch('https://api.exemplo.com/usuarios');
const dadosJSON = await resposta.text();
const dados = JSON.parse(dadosJSON);
setUsuarios(dados);
} catch (erro) {
setErro('Falha ao carregar dados');
}
}
carregarDados();
}, []);
if (erro) return <div>Erro: {erro}</div>;
return (
<ul>
{usuarios.map(usuario => (
<li key={usuario.id}>{usuario.nome}</li>
))}
</ul>
);
}
Salvando preferências no localStorage
import { useState, useEffect } from 'react';
function useTema() {
const [tema, setTema] = useState(() => {
// Carrega preferência salva
try {
const salvo = localStorage.getItem('tema');
return salvo ? JSON.parse(salvo) : 'claro';
} catch {
return 'claro';
}
});
useEffect(() => {
// Salva preferência quando muda
localStorage.setItem('tema', JSON.stringify(tema));
}, [tema]);
const alternarTema = () => {
setTema(prev => prev === 'claro' ? 'escuro' : 'claro');
};
return { tema, alternarTema };
}
Memoização de objetos JSON grandes
import { useMemo } from 'react';
function Dashboard({ dadosBrutos }) {
// Evita re-processamento desnecessário
const dadosProcessados = useMemo(() => {
try {
const dados = JSON.parse(dadosBrutos);
return dados.map(item => ({
...item,
dataFormatada: new Date(item.data).toLocaleDateString()
}));
} catch {
return [];
}
}, [dadosBrutos]);
return (
<div>
{dadosProcessados.map(item => (
<div key={item.id}>{item.nome} - {item.dataFormatada}</div>
))}
</div>
);
}
Lembre-se: JSON é onipresente no ecossistema JavaScript, mas requer cuidado com segurança, tipos especiais e validação de dados externos. Dominar essas práticas evita bugs sutis e vulnerabilidades em suas aplicações.
Referências
- MDN Web Docs: JSON.parse() — Documentação oficial completa sobre o método JSON.parse, incluindo exemplos com o parâmetro reviver.
- MDN Web Docs: JSON.stringify() — Referência detalhada do JSON.stringify com replacer e space.
- JSON.org: Introdução ao JSON — Especificação oficial do formato JSON, com exemplos e sintaxe.
- Node.js Documentation: File System — Documentação oficial do módulo fs para leitura e escrita de arquivos JSON.
- Zod Documentation — Biblioteca de validação de schema para TypeScript/JavaScript, ideal para validar dados JSON.
- OWASP: Prototype Pollution Prevention — Guia de segurança sobre prevenção de ataques de prototype pollution ao manipular objetos JSON.
- React Documentation: useEffect — Documentação oficial sobre o hook useEffect, usado para buscar e processar dados JSON em componentes React.