Voltar ao Glossario
Glossario Python

JSON: O que É e Como Funciona | Python Brasil

Aprenda a trabalhar com JSON em Python: serializar, deserializar, ler e gravar arquivos, APIs e boas praticas para manipulacao de dados estruturados.

O que e JSON?

JSON (JavaScript Object Notation) e um formato leve de troca de dados, facil de ler e escrever para humanos e simples de interpretar e gerar para maquinas. Apesar do nome fazer referencia a JavaScript, JSON e independente de linguagem e se tornou o formato padrao para comunicacao entre APIs, arquivos de configuracao e armazenamento de dados estruturados.

Python possui o modulo embutido json que oferece suporte completo para codificar (serializar) objetos Python em strings JSON e decodificar (deserializar) strings JSON em objetos Python.

Correspondencia de Tipos

A tabela de conversao entre tipos JSON e Python e direta:

import json

# JSON -> Python
# object -> dict
# array -> list
# string -> str
# number (int) -> int
# number (float) -> float
# true -> True
# false -> False
# null -> None

Serializacao (Python para JSON)

import json

# Dicionario para string JSON
dados = {
    'nome': 'Ana Silva',
    'idade': 28,
    'ativo': True,
    'habilidades': ['Python', 'SQL', 'Docker'],
    'endereco': {
        'cidade': 'Sao Paulo',
        'estado': 'SP',
    },
    'telefone': None,
}

# Converter para string JSON
json_str = json.dumps(dados)
print(json_str)

# Com formatacao legivel
json_formatado = json.dumps(dados, indent=2, ensure_ascii=False)
print(json_formatado)

# Gravar em arquivo
with open('dados.json', 'w', encoding='utf-8') as f:
    json.dump(dados, f, indent=2, ensure_ascii=False)

Deserializacao (JSON para Python)

import json

# String JSON para dicionario
json_str = '{"nome": "Bruno", "idade": 25, "ativo": true}'
dados = json.loads(json_str)
print(dados['nome'])     # 'Bruno'
print(type(dados))       # <class 'dict'>

# Ler de arquivo
with open('dados.json', 'r', encoding='utf-8') as f:
    dados = json.load(f)
print(dados)

# Lista JSON
json_lista = '[1, 2, 3, "quatro", null]'
lista = json.loads(json_lista)
print(lista)  # [1, 2, 3, 'quatro', None]

Serializacao de Tipos Customizados

O modulo json nao sabe serializar todos os tipos Python. Para tipos como datetime, Decimal e classes customizadas, e necessario criar um encoder personalizado.

import json
from datetime import datetime, date
from decimal import Decimal

# Usando default para tipos customizados
def serializador_customizado(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return float(obj)
    if isinstance(obj, set):
        return list(obj)
    raise TypeError(f'Tipo {type(obj)} nao e serializavel')

dados = {
    'criado_em': datetime.now(),
    'preco': Decimal('49.99'),
    'tags': {'python', 'tutorial'},
}

json_str = json.dumps(dados, default=serializador_customizado, indent=2)
print(json_str)

# Usando JSONEncoder customizado
class MeuEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return str(obj)
        return super().default(obj)

json_str = json.dumps(dados, cls=MeuEncoder, indent=2)

Trabalhando com APIs

import json
from urllib.request import urlopen, Request

# GET request
url = 'https://jsonplaceholder.typicode.com/posts/1'
with urlopen(url) as response:
    dados = json.loads(response.read().decode())
    print(dados['title'])

# POST request com JSON
dados_envio = {'title': 'Novo Post', 'body': 'Conteudo', 'userId': 1}
req = Request(
    'https://jsonplaceholder.typicode.com/posts',
    data=json.dumps(dados_envio).encode('utf-8'),
    headers={'Content-Type': 'application/json'},
    method='POST',
)
with urlopen(req) as response:
    resultado = json.loads(response.read().decode())
    print(resultado)

Validacao e Tratamento de Erros

import json

# Tratar JSON invalido
texto_invalido = "{'chave': 'valor'}"  # aspas simples nao sao JSON valido

try:
    dados = json.loads(texto_invalido)
except json.JSONDecodeError as e:
    print(f'Erro ao decodificar JSON: {e}')
    print(f'Linha: {e.lineno}, Coluna: {e.colno}')

# Funcao segura para parsing
def parse_json_seguro(texto: str, padrao=None):
    """Tenta parsear JSON, retorna valor padrao se falhar."""
    try:
        return json.loads(texto)
    except (json.JSONDecodeError, TypeError):
        return padrao

resultado = parse_json_seguro('invalido', padrao={})
print(resultado)  # {}

JSON Lines (JSONL)

import json

# JSONL: um objeto JSON por linha — util para logs e dados em streaming
registros = [
    {'id': 1, 'nome': 'Ana', 'nota': 9.5},
    {'id': 2, 'nome': 'Bruno', 'nota': 7.8},
    {'id': 3, 'nome': 'Carla', 'nota': 8.3},
]

# Gravar JSONL
with open('dados.jsonl', 'w') as f:
    for registro in registros:
        f.write(json.dumps(registro, ensure_ascii=False) + '\n')

# Ler JSONL
with open('dados.jsonl', 'r') as f:
    dados = [json.loads(linha) for linha in f if linha.strip()]

Opcoes Uteis de json.dumps

import json

dados = {'z_campo': 1, 'a_campo': 2, 'nome': 'Python'}

# sort_keys — ordena as chaves alfabeticamente
print(json.dumps(dados, sort_keys=True))

# separators — controla separadores para JSON compacto
compacto = json.dumps(dados, separators=(',', ':'))
print(compacto)  # {"z_campo":1,"a_campo":2,"nome":"Python"}

# ensure_ascii=False — preserva caracteres nao-ASCII
dados_br = {'cidade': 'Sao Paulo', 'descricao': 'Capital paulista'}
print(json.dumps(dados_br, ensure_ascii=False))

Erros Comuns

O erro mais frequente e tentar serializar tipos que o modulo json nao suporta nativamente, como datetime, Decimal, set e classes customizadas, sem fornecer um serializador. Outro erro e usar aspas simples em strings JSON manuais — JSON exige aspas duplas. Tambem e comum esquecer de especificar encoding='utf-8' ao ler ou gravar arquivos JSON com caracteres acentuados, e nao tratar JSONDecodeError ao receber dados de fontes externas.

Boas Praticas

Sempre use ensure_ascii=False ao trabalhar com texto em portugues para manter a legibilidade. Use indent=2 para arquivos de configuracao e dados que humanos vao ler. Para dados de API ou armazenamento, omita o indent para economizar espaco. Trate sempre excecoes ao parsear JSON de fontes externas. Para projetos que exigem validacao de esquema, considere bibliotecas como Pydantic.

Quando Usar

JSON e o formato padrao para troca de dados em APIs REST, arquivos de configuracao, comunicacao entre microservicos e armazenamento de dados semi-estruturados. Em Python, o modulo json e suficiente para a maioria dos casos. Para necessidades de alta performance, considere orjson ou ujson.