📜 JavaScript & JSON

💡 Origem do JSON

JSON é um subconjunto da sintaxe de objetos JavaScript. Por isso, trabalhar com JSON em JavaScript é muito simples e natural.

Visão Geral

Método Descrição Direção
JSON.parse() Converte string JSON em objeto JS JSON → JavaScript
JSON.stringify() Converte objeto JS em string JSON JavaScript → JSON

📥 JSON.parse()

Converte uma string JSON num objeto JavaScript.

Sintaxe

Sintaxe
const objeto = JSON.parse(textoJSON[, reviver]);

Exemplo Básico

Parse Simples
// String JSON recebida de API, ficheiro, etc.
const jsonString = '{"nome":"Ana","idade":25,"ativo":true}';

// Converter para objeto JavaScript
const pessoa = JSON.parse(jsonString);

// Agora podes aceder como objeto normal
console.log(pessoa.nome);   // "Ana"
console.log(pessoa.idade);  // 25
console.log(pessoa.ativo); // true

Parse de Arrays

Array JSON
const jsonArr = '[1, 2, 3, 4, 5]';
const numeros = JSON.parse(jsonArr);

console.log(numeros[0]); // 1
console.log(numeros.length); // 5

// Array de objetos
const jsonUsers = '[{"id":1,"nome":"Ana"},{"id":2,"nome":"Bruno"}]';
const users = JSON.parse(jsonUsers);

users.forEach(user => {
    console.log(user.nome);
});
// "Ana"
// "Bruno"

Reviver Function

A função reviver permite transformar valores durante o parse:

Reviver Function
const json = '{"nome":"Ana","nascimento":"1995-03-15"}';

const pessoa = JSON.parse(json, (key, value) => {
    // Se a chave for "nascimento", converter para Date
    if (key === 'nascimento') {
        return new Date(value);
    }
    return value;
});

console.log(pessoa.nascimento); // Date object
console.log(pessoa.nascimento.getFullYear()); // 1995

📤 JSON.stringify()

Converte um valor JavaScript numa string JSON.

Sintaxe

Sintaxe
const json = JSON.stringify(valor[, replacer[, space]]);

Exemplo Básico

Stringify Simples
const pessoa = {
    nome: "Ana",
    idade: 25,
    ativo: true
};

const json = JSON.stringify(pessoa);
console.log(json);
// '{"nome":"Ana","idade":25,"ativo":true}'

Formatação Legível

Indentação
const pessoa = {
    nome: "Ana",
    idade: 25,
    endereco: {
        cidade: "Lisboa",
        pais: "Portugal"
    }
};

// Com indentação (2 espaços)
const json = JSON.stringify(pessoa, null, 2);
console.log(json);
/*
{
  "nome": "Ana",
  "idade": 25,
  "endereco": {
    "cidade": "Lisboa",
    "pais": "Portugal"
  }
}
*/

Filtrar Propriedades

Replacer Array
const pessoa = {
    nome: "Ana",
    idade: 25,
    senha: "123456", // Não queremos incluir!
    email: "ana@exemplo.com"
};

// Apenas incluir certas propriedades
const json = JSON.stringify(pessoa, ['nome', 'email']);
console.log(json);
// '{"nome":"Ana","email":"ana@exemplo.com"}'

Replacer Function

Replacer Function
const pessoa = {
    nome: "Ana",
    senha: "123456",
    token: "abc123"
};

const json = JSON.stringify(pessoa, (key, value) => {
    // Remover campos sensíveis
    if (key === 'senha' || key === 'token') {
        return undefined; // Não incluir no JSON
    }
    return value;
});

console.log(json);
// '{"nome":"Ana"}'

toJSON Method

Personalizar Serialização
class Utilizador {
    constructor(nome, email) {
        this.nome = nome;
        this.email = email;
        this.senha = 'secreta'; // Não incluir no JSON
        this.criadoEm = new Date();
    }

    // Método especial para JSON
    toJSON() {
        return {
            nome: this.nome,
            email: this.email
            // senha e criadoEm omitidos
        };
    }
}

const user = new Utilizador('Ana', 'ana@exemplo.com');
console.log(JSON.stringify(user));
// '{"nome":"Ana","email":"ana@exemplo.com"}'

🌐 Fetch API e JSON

A Fetch API é a forma moderna de fazer pedidos HTTP em JavaScript.

Pedido GET

Fetch GET
// Buscar dados de uma API
fetch('https://jsonplaceholder.typicode.com/users/1')
    .then(response => {
        // Verificar se o pedido foi bem-sucedido
        if (!response.ok) {
            throw new Error('Erro na rede');
        }
        return response.json(); // Parse do JSON
    })
    .then(user => {
        console.log(user.name);
        console.log(user.email);
    })
    .catch(error => {
        console.error('Erro:', error);
    });

Async/Await

Async/Await
async function buscarUtilizador(id) {
    try {
        const response = await fetch(
            `https://jsonplaceholder.typicode.com/users/${id}`
        );

        if (!response.ok) {
            throw new Error(`HTTP error: ${response.status}`);
        }

        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Erro ao buscar:', error);
        throw error;
    }
}

// Uso
const user = await buscarUtilizador(1);
console.log(user.name);

Pedido POST

Fetch POST
async function criarPost(post) {
    const response = await fetch(
        'https://jsonplaceholder.typicode.com/posts',
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(post)
        }
    );

    if (!response.ok) {
        throw new Error('Erro ao criar post');
    }

    return await response.json();
}

// Uso
const novoPost = await criarPost({
    title: 'Meu Post',
    body: 'Conteúdo do post',
    userId: 1
});

console.log('Post criado com ID:', novoPost.id);

Tratamento de Erros Completo

Fetch com Tratamento Robusto
async function fetchComTratamento(url) {
    try {
        const response = await fetch(url);

        // Verificar status HTTP
        if (!response.ok) {
            if (response.status === 404) {
                throw new Error('Recurso não encontrado');
            } else if (response.status === 500) {
                throw new Error('Erro no servidor');
            }
            throw new Error(`HTTP ${response.status}`);
        }

        // Verificar Content-Type
        const contentType = response.headers.get('content-type');
        if (!contentType || !contentType.includes('application/json')) {
            throw new Error('Resposta não é JSON');
        }

        return await response.json();

    } catch (error) {
        if (error instanceof TypeError) {
            // Erro de rede (sem conexão, DNS, etc.)
            console.error('Erro de rede:', error.message);
        } else {
            console.error('Erro:', error.message);
        }
        throw error;
    }
}

💾 LocalStorage

LocalStorage só guarda strings. Usa JSON para guardar objetos.

Guardar Objeto

Guardar em LocalStorage
// Guardar objeto
const config = {
    tema: 'escuro',
    idioma: 'pt',
    notificacoes: true
};

// Converter para JSON e guardar
localStorage.setItem('config', JSON.stringify(config));

Recuperar Objeto

Ler de LocalStorage
// Ler e converter de volta para objeto
const json = localStorage.getItem('config');

if (json) {
    const config = JSON.parse(json);
    console.log(config.tema); // 'escuro'
} else {
    console.log('Config não encontrada');
}

Funções Utilitárias

Utilitários LocalStorage
// Guardar objeto
function guardar(chave, valor) {
    localStorage.setItem(chave, JSON.stringify(valor));
}

// Ler objeto
function ler(chave, valorPadrao = null) {
    const json = localStorage.getItem(chave);
    if (!json) return valorPadrao;

    try {
        return JSON.parse(json);
    } catch (e) {
        console.error('JSON inválido no localStorage:', e);
        return valorPadrao;
    }
}

// Remover
function remover(chave) {
    localStorage.removeItem(chave);
}

// Exemplo de uso
guardar('utilizador', { nome: 'Ana', id: 1 });
const user = ler('utilizador', {}); // {} se não existir
console.log(user.nome); // 'Ana'

Lista de Tarefas com LocalStorage

Exemplo Prático
class ListaTarefas {
    constructor() {
        this.tarefas = this.carregar();
    }

    carregar() {
        const json = localStorage.getItem('tarefas');
        return json ? JSON.parse(json) : [];
    }

    guardar() {
        localStorage.setItem('tarefas', JSON.stringify(this.tarefas));
    }

    adicionar(texto) {
        const tarefa = {
            id: Date.now(),
            texto: texto,
            feita: false,
            criadaEm: new Date().toISOString()
        };
        this.tarefas.push(tarefa);
        this.guardar();
        return tarefa;
    }

    marcarFeita(id) {
        const tarefa = this.tarefas.find(t => t.id === id);
        if (tarefa) {
            tarefa.feita = true;
            this.guardar();
        }
    }

    remover(id) {
        this.tarefas = this.tarefas.filter(t => t.id !== id);
        this.guardar();
    }

    listar() {
        return this.tarefas;
    }
}

// Uso
const lista = new ListaTarefas();
lista.adicionar('Aprender JSON');
lista.adicionar('Praticar JavaScript');
console.log(lista.listar());

🚨 Tratamento de Erros

Try/Catch

Try/Catch com JSON
function parseSeguro(jsonString) {
    try {
        const data = JSON.parse(jsonString);
        return { sucesso: true, data: data };
    } catch (error) {
        return {
            sucesso: false,
            erro: error.message,
            data: null
        };
    }
}

// Uso
const resultado = parseSeguro('{"nome":"Ana"}');
if (resultado.sucesso) {
    console.log(resultado.data.nome);
} else {
    console.log('JSON inválido:', resultado.erro);
}

Erros Comuns

Erro Causa Solução
SyntaxError JSON mal formatado Validar JSON antes de parse
TypeError Valor não é string Verificar tipo antes de parse
undefined Propriedade não existe Verificar com ?. ou hasOwnProperty

Validação

Validar JSON
function isValidJson(str) {
    try {
        JSON.parse(str);
        return true;
    } catch (e) {
        return false;
    }
}

// Uso
console.log(isValidJson('{"nome":"Ana"}')); // true
console.log(isValidJson('{nome:"Ana"}'));   // false
console.log(isValidJson('not json'));       // false

Acesso Seguro

Optional Chaining
const json = '{"empresa":{"endereco":{"cidade":"Lisboa"}}}';
const data = JSON.parse(json);

// Perigoso - pode lançar erro
// const cidade = data.empresa.endereco.cidade;

// Seguro com optional chaining
const cidade = data?.empresa?.endereco?.cidade ?? 'Desconhecida';

// Ou com verificação
if (data.empresa && data.empresa.endereco) {
    console.log(data.empresa.endereco.cidade);
}

💡 Exemplos Práticos

Deep Clone

Clone Profundo
function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj));
}

const original = {
    nome: 'Ana',
    endereco: { cidade: 'Lisboa' }
};

const copia = deepClone(original);

// Modificar cópia não afeta original
copia.endereco.cidade = 'Porto';
console.log(original.endereco.cidade); // 'Lisboa' (inalterado)

Comparar Objetos

Comparar JSON
function objetosIguais(obj1, obj2) {
    return JSON.stringify(obj1) === JSON.stringify(obj2);
}

const a = { x: 1, y: 2 };
const b = { x: 1, y: 2 };
const c = { y: 2, x: 1 }; // Ordem diferente

console.log(objetosIguais(a, b)); // true
console.log(objetosIguais(a, c)); // false (ordem importa!)

Debug

Debug com JSON
// Mostrar objeto de forma legível
function debug(obj, titulo = 'Debug') {
    console.log('=== ' + titulo + ' ===');
    console.log(JSON.stringify(obj, null, 2));
}

const dados = { users: [{ id: 1, nome: 'Ana' }] };
debug(dados, 'Utilizadores');

📝 Exercícios

▼

Exercício 1: Parse

Problema: Converte esta string JSON num objeto e mostra o nome da primeira pessoa:

[{"id":1,"nome":"Ana","idade":25},{"id":2,"nome":"Bruno","idade":30}]
const json = '[{"id":1,"nome":"Ana","idade":25},{"id":2,"nome":"Bruno","idade":30}]';
const pessoas = JSON.parse(json);
console.log(pessoas[0].nome); // "Ana"
▼

Exercício 2: Stringify Formatado

Problema: Converte este objeto para JSON formatado com 2 espaços de indentação:

const config = { tema: 'escuro', idioma: 'pt' };
const config = { tema: 'escuro', idioma: 'pt' };
const json = JSON.stringify(config, null, 2);
console.log(json);
/*
{
  "tema": "escuro",
  "idioma": "pt"
}
*/
▼

Exercício 3: Fetch API

Problema: Cria uma função async que busca um utilizador da API JSONPlaceholder e retorna o nome.

async function buscarNomeUtilizador(id) {
    const response = await fetch(
        `https://jsonplaceholder.typicode.com/users/${id}`
    );
    const user = await response.json();
    return user.name;
}

// Uso
const nome = await buscarNomeUtilizador(1);
console.log(nome); // "Leanne Graham"