JSON
Nesta aula, você aprenderá a trabalhar com JSON em Python, utilizando os módulos json e suas principais funções como json.load e json.dumps. Exploraremos a serialização de objetos Python, incluindo objetos customizados, e como tratar erros comuns durante o parsing e a serialização de dados JSON.
JSON (JavaScript Object Notation) é um formato de dados leve e amplamente utilizado para troca de informações entre sistemas. Em Python, o módulo json fornece funcionalidades para codificar (serializar) e decodificar (desserializar) dados nesse formato. Dominar o uso de JSON é essencial para trabalhar com APIs, armazenar configurações e persistir dados estruturados.
Nesta aula, vamos explorar as principais funções do módulo json, com foco em json.load e json.dumps, além de abordar a serialização de objetos customizados e o tratamento de erros. Ao final, você será capaz de integrar JSON em seus projetos com confiança.
json.load e json.dumps
A função json.dumps (dump string) converte um objeto Python (como dicionários, listas, strings, números, booleanos e None) em uma string JSON. Já json.load (load) lê dados JSON de um arquivo ou de uma string e os converte de volta para objetos Python. A função equivalente para strings é json.loads.
Vejamos um exemplo básico de serialização com json.dumps:
import json
dados = {
"nome": "Ana",
"idade": 30,
"ativo": True,
"tags": ["python", "json"],
"endereco": {
"cidade": "São Paulo",
"uf": "SP"
}
}
# Serializando para string JSON
json_string = json.dumps(dados)
print(json_string)
# Saída: {"nome": "Ana", "idade": 30, "ativo": true, "tags": ["python", "json"], "endereco": {"cidade": "São Paulo", "uf": "SP"}}
Para ler JSON de uma string, usamos json.loads:
json_texto = '{"nome": "Ana", "idade": 30}'
dados = json.loads(json_texto)
print(dados["nome"]) # Ana
Já json.load é utilizado para ler diretamente de um arquivo:
with open("dados.json", "r", encoding="utf-8") as arquivo:
dados = json.load(arquivo)
print(dados)
Da mesma forma, json.dump grava um objeto Python diretamente em um arquivo:
with open("saida.json", "w", encoding="utf-8") as arquivo:
json.dump(dados, arquivo, ensure_ascii=False, indent=4)
O parâmetro ensure_ascii=False garante que caracteres acentuados sejam preservados, e indent=4 formata o JSON para melhor legibilidade.
Serialização
Serialização é o processo de converter um objeto em memória (como um dicionário ou uma lista) em uma representação que possa ser armazenada ou transmitida. A desserialização é o processo inverso. O módulo json suporta serialização de tipos primitivos: dict, list, tuple, str, int, float, bool e None. Tuplas são convertidas em listas JSON.
É importante notar que o JSON não suporta todos os tipos de objetos Python. Por exemplo, objetos datetime ou classes definidas pelo usuário não são serializáveis por padrão. Para lidar com isso, precisamos personalizar o processo de serialização, como veremos a seguir.
Objetos customizados
Quando precisamos serializar objetos que não são nativamente suportados, podemos criar uma função de serialização personalizada e passá-la ao parâmetro default de json.dumps. Essa função deve converter o objeto para um tipo serializável, geralmente um dicionário.
Considere a seguinte classe:
class Pessoa:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def to_dict(self):
return {"nome": self.nome, "idade": self.idade}
Para serializar um objeto Pessoa, podemos fazer:
def default_serializer(obj):
if hasattr(obj, "to_dict"):
return obj.to_dict()
raise TypeError(f"Tipo não serializável: {type(obj)}")
p = Pessoa("Carlos", 25)
json_string = json.dumps(p, default=default_serializer)
print(json_string) # {"nome": "Carlos", "idade": 25}
Outra abordagem é usar o parâmetro default com uma função lambda:
json.dumps(p, default=lambda obj: obj.__dict__)
Para desserializar, podemos usar json.loads com o parâmetro object_hook para converter um dicionário de volta em um objeto customizado:
def decode_pessoa(dct):
if "nome" in dct and "idade" in dct:
return Pessoa(dct["nome"], dct["idade"])
return dct
dados = json.loads(json_string, object_hook=decode_pessoa)
print(dados.nome) # Carlos
Tratamento de erros
Ao trabalhar com JSON, erros podem ocorrer devido a dados malformados, tipos incompatíveis ou problemas de codificação. O módulo json lança exceções específicas que devem ser tratadas para tornar o código robusto.
As principais exceções são:
json.JSONDecodeError– ocorre quando a string ou arquivo não contém JSON válido.TypeError– ocorre quando um objeto não é serializável.FileNotFoundError– ao tentar abrir um arquivo inexistente.
Exemplo de tratamento de erro ao decodificar:
import json
texto = "{nome: 'Ana'}" # JSON inválido (faltam aspas nas chaves)
try:
dados = json.loads(texto)
except json.JSONDecodeError as e:
print(f"Erro de JSON: {e}")
print(f"Na linha {e.lineno}, coluna {e.colno}")
Para serialização, podemos capturar TypeError:
try:
json.dumps(set([1, 2, 3])) # set não é serializável
except TypeError as e:
print(f"Erro de serialização: {e}")
Além disso, é possível usar json.dumps com o parâmetro skipkeys=True para ignorar chaves que não são de tipos básicos (ex.: tuplas com elementos não serializáveis) em vez de lançar erro.
Boas práticas e observações finais
Ao trabalhar com JSON em Python, siga estas boas práticas:
- Use
ensure_ascii=Falsepara preservar caracteres acentuados. - Use
indent=2ouindent=4para melhorar a legibilidade ao gravar arquivos. - Valide os dados recebidos de fontes externas antes de processá-los.
- Em aplicações web, esteja ciente dos riscos de segurança ao carregar JSON de fontes não confiáveis (evite usar
eval). - Documente a estrutura esperada dos JSONs que sua aplicação consome ou produz.
Referências
- Documentação oficial do módulo json
- Tutorial de JSON em Python no Real Python
- JSON em Python no W3Schools
- Noções básicas de JSON (MDN)
- Introdução ao JSON em português
Exercícios
- Escreva um código que serialize um dicionário contendo uma lista de números e uma tupla de strings para JSON, e depois desserialize a string de volta para um dicionário. Imprima o tipo da tupla após a desserialização.
- Crie uma classe
Produtocom atributosnomeepreco. Implemente a serialização e desserialização de um objeto dessa classe usando as funçõesto_dicteobject_hook. - Escreva um programa que leia um arquivo JSON contendo uma lista de usuários (cada um com nome e email) e exiba apenas os emails. Trate possíveis erros de arquivo não encontrado e JSON inválido.
- Explique a diferença entre
json.dumpsejson.dump, e entrejson.loadsejson.load. Dê um exemplo de uso de cada um. - Qual exceção é lançada quando tentamos serializar um objeto do tipo
set? Como podemos contornar isso? Escreva um código que converta umsetem uma lista antes da serialização.
import json
dados = {
"numeros": [1, 2, 3],
"strings": ("a", "b", "c")
}
json_string = json.dumps(dados)
print(json_string) # {"numeros": [1, 2, 3], "strings": ["a", "b", "c"]}
decodificado = json.loads(json_string)
print(type(decodificado["strings"])) # <class 'list'>
import json
class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
def to_dict(self):
return {"nome": self.nome, "preco": self.preco}
def decode_produto(dct):
if "nome" in dct and "preco" in dct:
return Produto(dct["nome"], dct["preco"])
return dct
produto = Produto("Notebook", 2500.0)
json_str = json.dumps(produto, default=lambda obj: obj.to_dict())
print(json_str) # {"nome": "Notebook", "preco": 2500.0}
produto2 = json.loads(json_str, object_hook=decode_produto)
print(produto2.nome, produto2.preco) # Notebook 2500.0
import json
try:
with open("usuarios.json", "r", encoding="utf-8") as f:
usuarios = json.load(f)
for user in usuarios:
print(user["email"])
except FileNotFoundError:
print("Arquivo não encontrado.")
except json.JSONDecodeError as e:
print(f"Erro de JSON: {e}")
json.dumps serializa um objeto Python em uma string JSON. json.dump serializa e grava diretamente em um arquivo. json.loads desserializa uma string JSON em objeto Python. json.load lê de um arquivo e desserializa. Exemplos:
import json
dados = {"chave": "valor"}
# dumps: retorna string
s = json.dumps(dados)
# dump: escreve em arquivo
with open("dados.json", "w") as f:
json.dump(dados, f)
# loads: a partir de string
obj = json.loads(s)
# load: a partir de arquivo
with open("dados.json", "r") as f:
obj2 = json.load(f)
A exceção é TypeError. Podemos converter o set em lista antes de serializar:
import json
meu_set = {1, 2, 3}
try:
json.dumps(meu_set)
except TypeError as e:
print(f"Erro: {e}")
# Convertendo para lista
lista = list(meu_set)
json_str = json.dumps(lista)
print(json_str) # [1, 2, 3]