Python e JSON sono una coppia naturale.
Che tu stia costruendo una REST API con FastAPI
o Django,
elaborando pipeline di dati o semplicemente leggendo un file di configurazione, lavorerai con JSON continuamente.
La buona notizia: la libreria standard di Python ha tutto ciò di cui hai bisogno nel
modulo json.
Nessun pip install richiesto.
Le Quattro Funzioni che Usi Davvero
Il modulo json ti offre quattro funzioni per il lavoro quotidiano:
json.loads(str)— analizza una stringa JSON in un oggetto Pythonjson.dumps(obj)— converte un oggetto Python in una stringa JSONjson.load(file)— analizza JSON direttamente da un oggetto filejson.dump(obj, file)— scrive un oggetto Python come JSON su un file
La s in loads / dumps sta per stringa.
Quelle senza la s lavorano con oggetti file. Facile da ricordare una volta che conosci la regola.
json.loads() — Analisi di una Stringa JSON
import json
json_string = '{"name": "Alice", "age": 30, "active": true, "score": 98.5}'
user = json.loads(json_string)
print(user["name"]) # Alice
print(user["age"]) # 30
print(user["active"]) # True
print(type(user)) # <class 'dict'>Nota la mappatura dei tipi: JSON true diventa Python True,
JSON false diventa Python False, JSON null diventa Python None.
Gli oggetti JSON diventano dict Python, gli array JSON diventano list Python.
json.dumps() — Serializzazione in una Stringa JSON
import json
user = {
"name": "Bob",
"age": 25,
"roles": ["admin", "editor"],
"active": True,
"extra": None
}
# Compatto (ottimo per la trasmissione in rete)
compact = json.dumps(user)
print(compact)
# {"name": "Bob", "age": 25, "roles": ["admin", "editor"], "active": true, "extra": null}
# Formattato (ottimo per log e ispezione umana)
pretty = json.dumps(user, indent=2)
print(pretty)
# {
# "name": "Bob",
# "age": 25,
# "roles": [
# "admin",
# "editor"
# ],
# "active": true,
# "extra": null
# }Nota la mappatura inversa dei tipi: Python True → JSON true,
Python None → JSON null. Python gestisce questo automaticamente.
Lettura di JSON da un File
Questo è probabilmente il caso d'uso più comune — leggere un file di configurazione o dati all'avvio:
import json
# Leggi e analizza in un solo passaggio
with open("config.json", "r", encoding="utf-8") as f:
config = json.load(f)
print(config["database"]["host"]) # localhost
print(config["database"]["port"]) # 5432Specifica sempre encoding="utf-8" quando apri file JSON. JSON è specificato come UTF-8
dalla RFC 8259,
e omettere questa specifica può causare problemi su Windows dove la codifica predefinita è a volte cp1252.
Scrittura di JSON su un File
import json
results = {
"timestamp": "2024-01-15T09:30:00Z",
"total": 1523,
"processed": 1521,
"failed": 2,
"errors": [
{"id": 42, "reason": "missing field"},
{"id": 99, "reason": "invalid format"}
]
}
with open("results.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2)
print("Results saved to results.json")Gestione Corretta degli Errori
json.loads() solleva
json.JSONDecodeError
(una sottoclasse di ValueError) quando l'input non è JSON valido. Gestiscila sempre quando analizzi
dati che non controlli:
import json
def safe_parse(json_str):
try:
return json.loads(json_str)
except json.JSONDecodeError as e:
print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")
return None
data = safe_parse('{"name": "Alice"}') # works fine
bad = safe_parse('not json at all') # prints error, returns None
also_bad = safe_parse('{"key": }') # prints error with position infoJSONDecodeError ti fornisce la riga e la colonna esatte dove l'analisi è fallita,
il che è utile quando si esegue il debug di file JSON di grandi dimensioni.
Opzioni Utili di dumps()
import json
data = {
"z_key": 1,
"a_key": 2,
"price": 9.999999999
}
# Ordina le chiavi alfabeticamente (ottimo per output riproducibili / diff)
print(json.dumps(data, sort_keys=True, indent=2))
# {
# "a_key": 2,
# "price": 9.999999999,
# "z_key": 1
# }
# Assicura che i caratteri non-ASCII vengano preservati (predefinito: escape in \uXXXX)
data2 = {"city": "Münich", "greeting": "こんにちは"}
print(json.dumps(data2, ensure_ascii=False))
# {"city": "Münich", "greeting": "こんにちは"}
# Con ensure_ascii=True (predefinito):
print(json.dumps(data2))
# {"city": "M\u00fcnich", "greeting": "\u3053\u3093\u306b\u3061\u306f"}ensure_ascii=False è qualcosa che aggiungo sempre quando scrivo file JSON che
contengono testo non-ASCII. La versione con escape è tecnicamente JSON valido ma molto più difficile da leggere in un editor di testo.
Serializzazione di Oggetti Personalizzati
Per impostazione predefinita, json.dumps() non può serializzare istanze di classi personalizzate o
oggetti datetime.
Hai due opzioni: creare una sottoclasse di
json.JSONEncoder,
oppure convertire prima in un dict:
import json
from datetime import datetime, date
# Opzione 1: classe encoder personalizzata
class AppEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
return super().default(obj)
data = {"name": "Alice", "created_at": datetime(2024, 1, 15, 9, 30)}
print(json.dumps(data, cls=AppEncoder, indent=2))
# {
# "name": "Alice",
# "created_at": "2024-01-15T09:30:00"
# }
# Opzione 2: parametro default= (più semplice per conversioni occasionali)
print(json.dumps(data, default=str, indent=2)) # converte tutto lo sconosciuto in strUn Pattern Pratico: Caricamento di File di Configurazione
Ecco un pattern reale che uso in quasi ogni progetto Python — un caricatore di configurazione che legge un file JSON con valori predefiniti ragionevoli:
import json
import os
from pathlib import Path
DEFAULTS = {
"database": {"host": "localhost", "port": 5432},
"debug": False,
"log_level": "INFO"
}
def load_config(path="config.json"):
config = DEFAULTS.copy()
config_path = Path(path)
if config_path.exists():
with open(config_path, "r", encoding="utf-8") as f:
try:
user_config = json.load(f)
# Merge profondo: le impostazioni utente sovrascrivono i valori predefiniti
for key, value in user_config.items():
if isinstance(value, dict) and key in config:
config[key].update(value)
else:
config[key] = value
except json.JSONDecodeError as e:
print(f"Warning: config.json is invalid ({e.msg}), using defaults")
return config
config = load_config()
print(config["database"]["host"]) # localhost (or overridden value)Conclusioni
Il modulo json di Python copre tutto ciò di cui hai bisogno senza dipendenze esterne.
Le regole fondamentali: usa loads()/dumps() per le stringhe, load()/dump()
per i file, gestisci sempre JSONDecodeError quando analizzi dati esterni, e aggiungi
ensure_ascii=False quando i tuoi dati contengono caratteri non latini.
Per il debug dei dati JSON, il Formattatore JSON e il
Validatore JSON possono farti risparmiare molto tempo.