Curso de Python / Salvar e ler JSON com validação de dados
JSON, de JavaScript Object Notation, é um formato de texto para trocar dados estruturados, como objetos, arrays, strings e números. Em Python, json.load() e dump() trabalham com arquivos, enquanto loads() e dumps() trabalham com texto em memória; interpretar JSON com sucesso não comprova que os dados atendem às regras da aplicação.
Relacionar valores JSON e tipos Python
| JSON | Python após a leitura usual |
|---|---|
| Objeto, com pares de nome e valor. | dict. |
| Array. | list. |
| String. | str. |
| Número inteiro ou com parte fracionária. | int ou float, respectivamente. |
true, false e null. |
True, False e None. |
No texto JSON, nomes de propriedades e strings usam aspas duplas. Aspas simples, comentários de Python e vírgula depois do último item não fazem parte dessa sintaxe. Não tente corrigir uma leitura com eval(): ele interpreta código Python, não é um leitor JSON.
JSON também não guarda todos os tipos Python de maneira idêntica. Uma tupla gravada como array volta como lista; datas e valores Decimal precisam de uma política explícita de conversão. Para dados que exigem preservação de tipos específicos, escolha e documente uma representação, em vez de esperar uma cópia completa de qualquer objeto Python.
Salvar um registro com acento e ler novamente
Use uma pasta separada com dados descartáveis. O programa substitui expenses-demo.json a cada execução. Salve o código completo em json_demo.py, com UTF-8, e execute python json_demo.py no terminal dessa pasta, com o ambiente virtual selecionado se houver. Em macOS ou Ubuntu sem ambiente virtual, use python3 json_demo.py quando necessário. O módulo json faz parte da biblioteca padrão.
import json
from pathlib import Path
data = {"currency": "KRW", "expenses": [{"name": "café", "amount": 8000}]}
path = Path("expenses-demo.json")
path.write_text(json.dumps(data, ensure_ascii=False, indent=2, allow_nan=False), encoding="utf-8")
loaded = json.loads(path.read_text(encoding="utf-8"))
amount = loaded["expenses"][0]["amount"]
if type(amount) is not int or amount < 0:
raise ValueError("invalid amount")
print(loaded["expenses"][0]["name"])
print(amount)
A saída esperada é:
café
8000
data contém uma lista de despesas dentro de um dicionário. O nome café torna visível o tratamento de caracteres fora de ASCII. json.dumps() transforma a estrutura em uma string JSON, e write_text() grava essa string em UTF-8. Na leitura, read_text() fornece texto para json.loads(), que reconstrói os tipos básicos.
A expressão loaded["expenses"][0]["amount"] acessa o campo da primeira despesa. A condição exige exatamente int e um valor não negativo; por isso exclui bool, apesar de esse tipo ser uma subclasse de int. O exemplo usa uma estrutura conhecida: ele ainda não verifica se todos os caminhos de acesso existem e têm o formato esperado.
Distinguir análise sintática, validação e persistência
Um texto JSON válido pode conter uma lista vazia em expenses, omitir amount ou fornecer uma string no lugar do valor numérico. O acesso direto do exemplo pode então gerar IndexError ou KeyError, ou a validação pode rejeitar o tipo. Num importador geral, valide as chaves obrigatórias, a lista, cada objeto e o intervalo dos valores antes de processá-los.
type(amount) is int expressa o contrato estrito adotado aqui. Trocar por isinstance(amount, int) aceitaria booleanos e outras subclasses. Isso pode ser apropriado em outra interface, mas muda a política. Uma conversão automática também é diferente de uma validação: transformar um valor recebido pode ocultar um formato que deveria ser recusado.
| Função | Entrada e resultado |
|---|---|
load(file) |
Lê JSON de um objeto de arquivo e retorna dados Python. |
loads(text) |
Interpreta texto JSON em memória. |
dump(obj, file) |
Grava a representação JSON num objeto de arquivo textual. |
dumps(obj) |
Retorna uma string JSON; não cria um arquivo por conta própria. |
Python permite certas extensões numéricas na configuração padrão, como NaN e infinitos. allow_nan=False no exemplo os rejeita ao serializar; essa opção não configura uma leitura posterior nem valida todos os campos. Ao receber dados externos estritos, defina também uma política de leitura para constantes não padronizadas e nomes de propriedades repetidos. Por padrão, o leitor conserva o último valor de uma chave repetida. A referência do módulo json e suas opções de codificação e decodificação descreve esses comportamentos.
Testar caracteres, erros e valores inválidos
ensure_ascii=False conserva caracteres como é diretamente na string, em vez de representá-los por escapes Unicode. Ele não escolhe a codificação do arquivo, que continua sendo definida por encoding="utf-8". As duas representações podem reconstruir o mesmo texto depois da leitura. indent=2 organiza a apresentação com recuos e não altera o significado dos dados.
- Execute o programa e abra
expenses-demo.jsonnum editor UTF-8. Confira o nomecafé, a lista de despesas e o valor 8000. Repita a execução e confirme que ela substitui o arquivo, sem concatenar documentos. - Na estrutura Python da linha
data = ..., substitua somente"amount": 8000por"amount": -1. O programa grava e lê o JSON, mas depois rejeita o valor comValueError: invalid amount. Restaure 8000 antes do teste seguinte. - No mesmo ponto da estrutura Python, use
"amount": True, com T maiúsculo. O serializador gravatrueno JSON; depois da leitura ele volta a serTruee a validação também o rejeita. Escrevertruediretamente no código Python seria outra situação, normalmente um nome não definido. - Restaure o valor 8000 e troque temporariamente
ensure_ascii=Falseporensure_ascii=True. O arquivo passa a usar um escape para é, mas a saída continua mostrandocafé. Restaure a opção original para comparar a apresentação.
Uma sintaxe JSON incorreta produz JSONDecodeError, cuja mensagem informa a posição do problema. Para testar um arquivo editado manualmente, execute apenas uma rotina de leitura separada: rodar este programa completo primeiro recria o arquivo e desfaz a edição. Dados inválidos também já podem ter sido gravados antes de a validação do exemplo falhar; se a política exigir não persistir entradas inválidas, valide a estrutura antes da escrita.
A gravação usada aqui é uma substituição simples num arquivo descartável. Para preservar arquivos importantes, aplique uma política de escrita e recuperação adequada, como discutido na leitura e escrita de texto com fechamento e proteção do original. O fato de a string ser JSON válido não garante integridade de armazenamento nem validade dos valores de negócio.




