Python JSON
Scopri come usare il modulo json di Python per codificare, decodificare, leggere e scrivere dati JSON, con esempi pratici e gestione degli errori.
Il modulo json integrato in Python ti consente di convertire oggetti Python in testo JSON e viceversa con appena due funzioni nella maggior parte dei casi. Questo capitolo copre tutto il necessario: mappatura dei tipi di dati, dumps/loads per le stringhe, dump/load per i file, stampa formattata, gestione degli errori e serializzazione personalizzata.
Cos'è JSON?
JSON (JavaScript Object Notation) è un formato di scambio dati leggero e testuale. È leggibile dagli esseri umani, indipendente dal linguaggio e il formato predefinito per la maggior parte delle API web.
Un documento JSON è composto da due strutture:
- Oggetti — raccolte non ordinate di coppie chiave/valore racchiuse in
{}. Le chiavi sono sempre stringhe tra virgolette doppie. - Array — sequenze ordinate di valori racchiuse in
[].
Esempio di documento JSON:
{
"name": "John Doe",
"age": 30,
"city": "New York",
"hobbies": ["reading", "traveling", "photography"],
"active": true,
"score": null
}Mappatura dei tipi Python–JSON
Quando codifichi o decodifichi JSON, il modulo json di Python converte i tipi in base a questa tabella:
| Tipo Python | Tipo JSON | Tipo Python (dopo la decodifica) |
|---|---|---|
dict | object {} | dict |
list, tuple | array [] | list |
str | string "" | str |
int, float | number | int o float |
True / False | true / false | bool |
None | null | None |
Nota che le tuple diventano array JSON e ritornano come liste Python dopo la decodifica.
Codifica di oggetti Python in stringhe JSON
json.dumps() (dump-string) serializza un oggetto Python in una stringa formattata JSON.
Codifica di base
Output:
{"name": "John Doe", "age": 30, "city": "New York", "hobbies": ["reading", "traveling", "photography"]}Stampa formattata con indent e sort_keys
Per impostazione predefinita json.dumps() produce una stringa compatta su una singola riga. Passa indent per ottenere output leggibile dagli esseri umani, e sort_keys=True per ordinare le chiavi del dizionario in ordine alfabetico:
import json
person = {
"name": "John Doe",
"age": 30,
"city": "New York",
"hobbies": ["reading", "traveling", "photography"]
}
print(json.dumps(person, indent=2, sort_keys=True))Output:
{
"age": 30,
"city": "New York",
"hobbies": [
"reading",
"traveling",
"photography"
],
"name": "John Doe"
}Usa indent=2 o indent=4 quando scrivi file di configurazione o fai debug delle risposte API — rende le strutture annidate facili da leggere.
Decodifica di stringhe JSON in oggetti Python
json.loads() (load-string) analizza una stringa JSON e restituisce l'equivalente oggetto Python.
Decodifica di base
Output:
{'name': 'John Doe', 'age': 30, 'city': 'New York', 'hobbies': ['reading', 'traveling', 'photography']}
<class 'dict'>
John DoeIl valore decodificato è un normale dizionario Python, quindi puoi accedere alle sue chiavi con [] o .get(), iterarlo con for e così via.
Gestione di JSON non valido
Se l'input non è JSON valido, json.loads() solleva json.JSONDecodeError. Cattura sempre questo errore quando lavori con dati provenienti da fonti esterne:
import json
raw = '{"name": "Alice", "age":}' # invalid — missing value
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")Output:
Invalid JSON: Expecting value: line 1 column 24 (char 23)Consulta il capitolo Python try/except per una guida completa alla gestione delle eccezioni.
Lavorare con JSON annidato
Gli oggetti JSON possono contenere altri oggetti e array a qualsiasi profondità. Accedi ai valori annidati usando la notazione [] concatenata:
Output:
John
travelingPer strutture profondamente annidate, considera l'uso di .get() con un valore predefinito per evitare KeyError su chiavi mancanti:
city = person.get("address", {}).get("city", "unknown")Il capitolo sui dizionari annidati illustra i pattern per lavorare con dizionari Python profondamente annidati.
Lettura e scrittura di file JSON
json.dump() scrive su un oggetto file, e json.load() legge da uno. Questi sono i corrispondenti per file di dumps/loads.
Scrittura di JSON su un file
import json
data = {"name": "Alice", "scores": [95, 87, 92]}
with open("data.json", "w") as f:
json.dump(data, f, indent=2)Questo crea data.json con contenuto formattato. L'uso di with open(...) garantisce che il file venga chiuso automaticamente — consulta la gestione dei file Python per i dettagli.
Lettura di JSON da un file
import json
with open("data.json") as f:
data = json.load(f)
print(data)
print(data["scores"])Output (assumendo il file scritto sopra):
{'name': 'Alice', 'scores': [95, 87, 92]}
[95, 87, 92]Esempio di ciclo completo
import json
# Write
config = {"host": "localhost", "port": 5432, "debug": False}
with open("config.json", "w") as f:
json.dump(config, f, indent=2)
# Read back
with open("config.json") as f:
loaded = json.load(f)
print(loaded["port"]) # 5432
print(type(loaded["port"])) # <class 'int'>Recupero di JSON da un'API web
Nella maggior parte dei progetti decodificherai JSON proveniente da una risposta HTTP. La popolare libreria requests rende questo semplice:
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
response.raise_for_status() # raises an error for 4xx/5xx responses
data = response.json() # equivalent to json.loads(response.text)
print(data["title"])
print(data["completed"])response.json() chiama json.loads() internamente. Chiama sempre raise_for_status() prima di analizzare la risposta, così una richiesta fallita non restituisce silenziosamente un corpo di errore.
Serializzazione personalizzata con default
Il modulo json non è in grado di codificare oggetti Python arbitrari (come datetime.date) per impostazione predefinita. Passa un callable default o una sottoclasse JSONEncoder personalizzata per gestirli:
import json
import datetime
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.date):
return obj.isoformat()
return super().default(obj)
event = {"name": "Conference", "date": datetime.date(2024, 1, 15)}
print(json.dumps(event, cls=DateEncoder))Output:
{"name": "Conference", "date": "2024-01-15"}L'oggetto date viene convertito nella sua stringa ISO 8601 prima della serializzazione.
Deserializzazione personalizzata con object_hook
object_hook viene chiamato per ogni oggetto JSON (dict) durante la decodifica. Puoi usarlo per trasformare i dati al volo — per esempio, convertendo valori stringa nei tipi Python corretti:
import json
def as_record(d):
"""Convert age field from string to int if present."""
if "age" in d:
d["age"] = int(d["age"])
return d
raw = '{"name": "Bob", "age": "25"}'
person = json.loads(raw, object_hook=as_record)
print(person)
print(type(person["age"])) # <class 'int'>Output:
{'name': 'Bob', 'age': 25}
<class 'int'>Riferimento rapido
| Funzione | Direzione | Sorgente/destinazione |
|---|---|---|
json.dumps(obj) | Python → JSON | restituisce una str |
json.loads(s) | JSON → Python | legge da una str |
json.dump(obj, f) | Python → JSON | scrive su un file |
json.load(f) | JSON → Python | legge da un file |
Parametri opzionali principali:
indent=2— stampa formattata con rientro di 2 spazisort_keys=True— ordina le chiavi del dizionario in ordine alfabeticocls=MyEncoder— usa una sottoclasseJSONEncoderpersonalizzataobject_hook=fn— trasforma ogni object dict decodificato