Da JSON a Python: dataclass, Pydantic e TypedDict (2026)
Incolla il tuo JSON nel convertitore da JSON a Python e copia le classi che genera. È la via rapida per trasformare JSON in Python: niente da installare, niente da caricare, tutto gira nel tuo browser. Per un modello una tantum o un’occhiata veloce a una risposta di API che non conosci, hai finito in pochi secondi.
Il problema più difficile è quello che un convertitore non può risolvere al posto tuo. Deduce i tipi corretti, ma non può prendere la decisione che conta di più: quale dei tre output vuoi davvero. Un dataclass, un BaseModel di Pydantic v2 e un TypedDict possono descrivere esattamente lo stesso JSON, eppure differiscono per solidità della type-safety, comportamento a runtime e modo in cui ci carichi i dati. Scegli male e finisci per trascinarti dietro la dipendenza da Pydantic per nulla, oppure dai per scontato che un dataclass validi il tuo input quando non lo fa affatto.
Questa guida copre la matrice decisionale per tutti e tre, come il convertitore deduce i tipi, la gestione del camelCase in ciascuna modalità, come effettuare il parsing del JSON con la classe che generi (compresa la trappola dell’annidamento che frega tutti), Pydantic v2 contro v1 e i casi limite che vale la pena conoscere prima di fidarti dell’output.
Come convertire JSON in Python
Convertire JSON in Python richiede tre passaggi:
- Incolla il tuo JSON. Trascina un oggetto, un array o una risposta di API grezza nella casella di input. La conversione parte all’istante e interamente lato client.
- Scegli dataclass, Pydantic o TypedDict. Usa il selettore Output per cambiare stile e rinomina il
Rootpredefinito in qualcosa di significativo comeUseroApiResponse. - Copia o scarica. Prendi il Python generato con un clic e metti
models.pydirettamente nel tuo progetto.
Questo è l’intero flusso. Se il tuo input è minificato o non sei sicuro che sia valido, passalo prima in un formattatore JSON così il convertitore ha del JSON pulito e ben formato da leggere. Il resto di questa guida spiega l’output, così puoi sistemare i casi che uno strumento non è in grado di dedurre da solo.
Come i tipi JSON si mappano su Python
Ogni valore JSON ha una controparte in Python, e la corrispondenza è diretta:
| Valore JSON | Tipo Python |
|---|---|
"text" | str |
42 | int (precisione arbitraria, nessun overflow) |
3.14, 2e3 | float |
true / false | bool |
null | Optional[T] / Optional[Any] |
[1, 2, 3] | List[T] |
{ ... } | una classe con nome |
Prendi un tipico payload REST:
{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }
In modalità dataclass il convertitore produce una classe pronta all’uso:
from dataclasses import dataclass
from typing import List
@dataclass
class Root:
id: int
name: str
email: str
active: bool
roles: List[str]
Una sfumatura distingue Python dagli altri. Il suo int ha precisione arbitraria, quindi uno snowflake di Discord o qualsiasi ID oltre 2^53 resta un semplice int senza perdite. JavaScript perderebbe precisione sullo stesso valore, e Rust ti costringerebbe a scegliere tra i64, u64 e f64. Solo un token scritto con un punto decimale o un esponente viene dedotto come float. Un campo che è sempre e solo null non può essere tipizzato dal solo campione, quindi ripiega su Optional[Any]; incolla un payload rappresentativo con un valore reale per ottenere qualcosa di più preciso di Any.
dataclass vs Pydantic v2 vs TypedDict — quale usare?
Questa è la scelta che il convertitore rimette a te, ed è il punto centrale di questa guida. In breve: opta per un dataclass quando vuoi un contenitore di dati interno a zero dipendenze, Pydantic v2 quando effettui il parsing o la validazione di input non affidabili, e TypedDict quando ti servono solo suggerimenti di tipo statici su dict che hai già.
| Dimensione | dataclass | Pydantic v2 | TypedDict |
|---|---|---|---|
| Dipendenza | Libreria standard | pip install pydantic | Libreria standard (typing) |
| Validazione a runtime | No | Sì — valida e converte | No — solo suggerimenti |
| Costo a runtime | Leggero (oggetto reale) | Un po’ (validazione) | Zero (è un dict) |
| Parsing automatico annidato | No, manuale | Sì, model_validate è ricorsivo | No, è un dict |
| Alias camelCase | Mantiene la chiave originale | Field(alias=...) | Sintassi funzionale |
| Ideale per | Strutture interne | Risposte di API, webhook, config | Tipizzare codice dict legacy |
I tre target sembrano quasi identici per una forma piccola come {"id": 101, "name": "Ada Lovelace", "active": true}. Un dataclass da JSON a Python ti dà un oggetto della libreria standard:
from dataclasses import dataclass
@dataclass
class Root:
id: int
name: str
active: bool
La versione da JSON a Pydantic mette al suo posto BaseModel, che sembra simile ma si guadagna il posto a runtime:
from pydantic import BaseModel
class Root(BaseModel):
id: int
name: str
active: bool
E un TypedDict Python da JSON descrive la forma di un semplice dict senza allocare nulla di nuovo:
from typing import TypedDict
class Root(TypedDict):
id: int
name: str
active: bool
Stessi campi, tre contratti molto diversi. Il valore di generare tutti e tre da un unico campione è che puoi alternarli nello strumento e abbinare la modalità al compito: Pydantic quando fai il parsing di dati che non controlli, un dataclass quando i dati sono interni, e TypedDict quando vuoi solo che mypy capisca un dict che già passi in giro.
Come il convertitore deduce le classi
Tre regole coprono quasi tutto ciò che gli dai in pasto: una classe per ogni forma di oggetto, unione chiave per chiave per gli array, e una gestione attenta dei valori mancanti.
Deduzione strutturale: una classe con nome per ogni oggetto
Ogni forma di oggetto distinta diventa la propria classe con nome. Gli oggetti annidati non vengono incorporati in linea; vengono estratti in definizioni separate e referenziate:
{ "repo": "pydantic", "owner": { "login": "samuelcolvin", "id": 100 } }
from dataclasses import dataclass
@dataclass
class Owner:
login: str
id: int
@dataclass
class Root:
repo: str
owner: Owner
L’owner annidato diventa la sua classe Owner, referenziata dal campo. Nota l’ordine: la classe figlia viene emessa prima del genitore che la usa, così il modulo funziona senza from __future__ import annotations. Anche le forme identiche vengono deduplicate, quindi due campi con la stessa struttura condividono un’unica classe invece di produrre copie.
Unione degli array e campi Optional
Quando passi un array di oggetti, il convertitore li unisce chiave per chiave in un unico tipo di elemento. Una chiave presente in alcuni elementi ma assente in altri diventa Optional:
{ "users": [{ "id": 1, "nick": "x" }, { "id": 2 }] }
from typing import List, Optional, TypedDict
class User(TypedDict):
id: int
nick: Optional[str]
class Root(TypedDict):
users: List[User]
id compare in ogni elemento, quindi resta obbligatorio. nick è assente dal secondo utente, quindi diventa Optional[str]. È esattamente per questo che un singolo oggetto è un campione debole: lo strumento può marcare un campo come Optional solo quando vede davvero la chiave mancare da qualche parte. Dagli in pasto un array rappresentativo e la deduzione rispecchierà la forma reale.
Optional, None e array vuoti
Alcuni valori non portano con sé alcun tipo, e il convertitore è onesto al riguardo. Una chiave mancante da alcuni elementi dell’array diventa Optional[T]. Un campo che è sempre e solo null diventa Optional[Any], perché il null di JSON da solo non ti dice nulla sul tipo previsto. Un array vuoto o con tipi di elemento misti ripiega su List[Any]. Nessuno di questi è un bug; è lo strumento che si rifiuta di tirare a indovinare. Sostituisci ogni Any con un tipo concreto non appena hai un campione che ne mostra uno, e ricorda che l’int a precisione arbitraria di Python significa che un ID grande non ti costringe mai a un tipo numerico più ampio, come invece accadrebbe in Rust o JavaScript.
Gestire le chiavi camelCase in ciascuna modalità
Le chiavi JSON sono spesso in camelCase mentre i campi Python idiomatici sono in snake_case. Qui non c’è un’unica risposta giusta, quindi ogni modalità la risolve in modo diverso, e vale la pena capire la differenza prima di copiare l’output.
dataclass. Un dataclass non ha alcun meccanismo di alias integrato. Per mantenere funzionante Root(**data), il convertitore mantiene la chiave originale come nome del campo ogni volta che è un identificatore Python valido, quindi publicRepos resta publicRepos. Forzarlo a snake_case romperebbe **data con un TypeError.
Pydantic v2. Qui ottieni il risultato idiomatico. I campi vengono rinominati in snake_case con un Field(alias=...) che rimanda alla chiave JSON esatta, più model_config = ConfigDict(populate_by_name=True) così puoi costruire il modello per nome del campo o per alias:
from pydantic import BaseModel, ConfigDict, Field
class Root(BaseModel):
model_config = ConfigDict(populate_by_name=True)
login: str
public_repos: int = Field(alias="publicRepos")
created_at: str = Field(alias="createdAt")
È questo che consente a Root.model_validate(api_json) di gestire payload camelCase reali in andata e ritorno mentre il tuo codice si legge in modo naturale.
TypedDict. Le chiavi che sono semplici identificatori vengono usate direttamente. Non appena compare una chiave non-identificatore, il convertitore passa alla sintassi funzionale TypedDict('User', {...}) per preservare la chiave esatta, argomento della prossima sezione.
Parole chiave Python e chiavi non-identificatore
Le chiavi JSON sono semplici stringhe, quindi nulla impedisce a un’API di inviare class, from o first-name. Ognuna sarebbe un errore di sintassi come nudo attributo Python, quindi il convertitore le sanifica.
Una chiave che è una parola chiave Python riceve un underscore finale: class diventa class_, from diventa from_. In modalità Pydantic mantiene anche un Field(alias=...) così il modello legge comunque la chiave originale trasmessa:
from pydantic import BaseModel, ConfigDict, Field
class Root(BaseModel):
model_config = ConfigDict(populate_by_name=True)
class_: str = Field(alias="class")
from_: str = Field(alias="from")
Le chiavi con trattini, spazi o una cifra iniziale vengono sanificate in identificatori validi nelle modalità dataclass e Pydantic. TypedDict è l’unica modalità in grado di mantenere la chiave letterale, usando la forma funzionale così "first-name" sopravvive intatta:
from typing import TypedDict
Root = TypedDict("Root", {"first-name": str, "2fa": bool})
Il risultato funziona sempre, e in modalità TypedDict la chiave resta byte per byte identica a ciò che l’API ha inviato.
Fare il parsing del JSON con la classe generata
Generare la classe è metà del lavoro. Caricarci dentro il JSON è il punto in cui le tre modalità divergono nettamente, e in cui un’assunzione diffusa si rivela sbagliata.
dataclass. Per un oggetto piatto, json.loads più Root(**data) funziona direttamente:
import json
from dataclasses import dataclass
from typing import List
@dataclass
class Root:
id: int
name: str
roles: List[str]
data = json.loads('{"id": 101, "name": "Ada Lovelace", "roles": ["admin"]}')
root = Root(**data)
print(root.name, root.roles)
Ma un dataclass non è ricorsivo. Se il JSON ha oggetti annidati, Root(**data) riempie il livello superiore e lascia ogni figlio come semplice dict, non la classe Owner che hai generato. Per fare il parsing di un intero albero, costruisci tu stesso i figli, usa una libreria come dacite o pydantic.dataclasses, oppure passa lo strumento in modalità Pydantic. Questa è l’insidia che la maggior parte dei convertitori non menziona mai.
Pydantic v2. Una sola chiamata fa il parsing e valida l’intero albero:
import json
from pydantic import BaseModel
class Root(BaseModel):
id: int
name: str
root = Root.model_validate(json.loads('{"id": 101, "name": "Ada Lovelace"}'))
print(root.name)
Una discordanza di tipo solleva un chiaro ValidationError invece di passare in silenzio. È questa la ricompensa per la dipendenza: il modello è un validatore a runtime, quindi una chiamata a model_validate che ritorna è una garanzia che i dati corrispondevano, non solo la convinzione di un type checker che lo facessero.
TypedDict. A runtime un TypedDict è un normale dict. Annotare data: Root = json.loads(text) serve puramente al type checker statico; nulla viene validato quando il codice gira. Se ti serve una vera applicazione delle regole, esegui mypy in CI oppure passa a Pydantic. Scegliere una modalità significa scegliere quanta garanzia a runtime vuoi, e quando un contratto deve andare oltre i tipi, la guida alla validazione con JSON Schema spiega come far rispettare la struttura da un capo all’altro.
Pydantic v2 vs v1 — cosa è cambiato
Il convertitore produce sintassi v2. Parecchie codebase usano ancora la v1, e incollare l’output v2 in un progetto v1 fallisce sui metodi rinominati più che sulla logica. Ecco la corrispondenza:
| Operazione | Pydantic v2 (output dello strumento) | Pydantic v1 |
|---|---|---|
| Parsing di un dict | model_validate(data) | parse_obj(data) |
| Parsing di JSON grezzo | model_validate_json(text) | parse_raw(text) |
| Configurare il modello | model_config = ConfigDict(...) | class Config interna |
| Serializzare in dict | model_dump() | dict() |
| Serializzare in JSON | model_dump_json() | json() |
Anche il comportamento degli alias si è irrigidito: in v2 ti serve populate_by_name=True insieme a Field(alias=...) per costruire un modello sia per nome del campo sia per alias, ed è per questo che lo strumento lo include. Passa alla v2 se puoi, perché è più veloce ed è il bersaglio dello sviluppo attuale; altrimenti riscrivi model_validate in parse_obj e ConfigDict in una Config interna.
json-to-python vs quicktype vs datamodel-code-generator vs manuale
Non esiste un unico modo migliore per generare una classe Python da JSON. Dipende da dove vive il JSON e da cosa parti.
| Approccio | Ideale per | Nota |
|---|---|---|
| Convertitore online (questo strumento, jsonlint, codeshack) | Conversioni una tantum, payload sensibili, zero installazioni | Deduce da un campione, interamente lato client |
| quicktype | Output multi-linguaggio, codegen in pipeline | Anch’esso guidato da un campione; il supporto Python è più povero su alias e scelta della modalità |
| datamodel-code-generator | Generare Pydantic da un Schema JSON o OpenAPI | L’input è uno schema, non un campione — più autorevole quando ne hai uno |
| Scrivere le classi a mano | Payload minuscoli, imparare la sintassi | Controllo totale, ma noioso e facile alla deriva |
La distinzione: un convertitore online e quicktype deducono entrambi i tipi da un campione di JSON, mentre datamodel-code-generator legge uno JSON Schema o una specifica OpenAPI come sua fonte di verità. Usa gli strumenti basati su campione per l’esplorazione e i casi una tantum; ricorri a quello guidato dallo schema quando un contratto esiste già. Se la tua codebase è in TypeScript invece che in Python, lo stesso approccio basato su campione si applica con il convertitore da JSON a TypeScript, e la guida alle interfacce da JSON a TypeScript approfondisce quel versante. Per l’aspetto della validazione a runtime in un linguaggio compilato e fortemente tipizzato, la guida alle struct da JSON a Rust offre un contrasto utile: serde è il Pydantic di Rust.
Insidie comuni nel generare Python da JSON
Le classi generate sono un punto di partenza. Fai attenzione a queste cose prima di affidarti all’output con dati reali.
- Un solo campione rivela di rado ogni forma.
Optionalpuò essere dedotto solo da elementi dell’array che differiscono davvero. Incolla un array rappresentativo così l’opzionalità è accurata, non un’ipotesi tratta da un oggetto fortunato. - Un dataclass non valida.
Root(**data)si limita ad assegnare i campi; non controlla alcun tipo e non ricorre in nulla. Se ti serve l’applicazione delle regole, usa Pydantic. - Il JSON annidato rompe
Root(**data). Viene popolato solo il livello superiore; i figli restano dict. Passa amodel_validatedi Pydantic, oppure usa dacite. - Un campo sempre
nulldiventaOptional[Any]. Trattalo come un segnaposto, non come un normale opzionale, e dagli un tipo reale non appena ne conosci uno. - Output v2 in un progetto v1.
model_validatenon sarà definito. Passa alla v2 o riscrivilo comeparse_obj. - Una data lasciata come
strnon farà calcoli tra date. Cambia il campo indatetime, oppure in Pydantic usa un campodatetimecosì effettua il parsing dell’ISO 8601 per te.
Domande frequenti
Dovrei usare un dataclass, Pydantic o TypedDict per il JSON?
Usa un dataclass per un contenitore di dati interno a zero dipendenze. Usa Pydantic v2 quando fai il parsing o validi input non affidabili, perché applica i tipi a runtime. Usa TypedDict per aggiungere suggerimenti statici a un dict che hai già, senza costo a runtime. Quando i dati provengono da fuori del tuo controllo, scegli Pydantic come opzione predefinita.
Pydantic valida il JSON a runtime?
Sì. Root.model_validate(data) valida e converte i tipi mentre il codice gira, sollevando un ValidationError in caso di input errato. Un dataclass non fa né l’una né l’altra cosa — Root(**data) si limita ad assegnare i campi. Nemmeno un TypedDict lo fa; è un suggerimento per i type checker statici come mypy e si comporta come un semplice dict a runtime.
Come tipizzo un campo che il JSON a volte omette?
Tipizzalo come Optional[T]. Quando una chiave compare solo in alcuni elementi dell’array, il convertitore la marca automaticamente come Optional. In Pydantic, assegna al campo un valore predefinito come = None così una chiave mancante non fa scattare un errore di validazione. Un campo dataclass può avere un valore predefinito allo stesso modo con field(default=None).
Perché il mio dataclass non è riuscito a fare il parsing del JSON annidato?
Perché Root(**data) non è ricorsivo. Riempie i campi di primo livello e lascia gli oggetti annidati come semplici dict invece delle classi figlie che hai generato. Per fare il parsing dell’intero albero in una sola chiamata, passa a model_validate di Pydantic, oppure aggiungi la ricorsione con dacite o pydantic.dataclasses.
In quale tipo Python si trasforma un intero JSON grande?
int. Gli interi di Python hanno precisione arbitraria, quindi uno snowflake o un ID Discord oltre 2^53 resta un semplice int senza perdite. Non incontri mai il calo di precisione di JavaScript, e non devi mai scegliere tra i64 e u64 come ti costringe a fare Rust.
Come vengono gestite le chiavi JSON in camelCase in ciascuna modalità?
Un dataclass mantiene la chiave originale così Root(**data) continua a funzionare. Pydantic rinomina i campi in snake_case con Field(alias="camelKey") più populate_by_name=True, così gestisce in andata e ritorno dati reali di API. TypedDict usa la sintassi funzionale TypedDict('Name', {...}) per preservare qualsiasi chiave che non sia un identificatore valido.
Come gestisco una chiave JSON che è una parola chiave Python come class?
Il convertitore aggiunge un underscore finale, trasformando class in class_ così il codice è valido. In modalità Pydantic aggiunge anche Field(alias="class") che rimappa il campo alla chiave originale trasmessa, così il nome è legale e il modello legge comunque il payload reale.
Il mio JSON è privato quando uso un convertitore online da JSON a Python?
Sì. La conversione gira al 100% nel tuo browser con JavaScript. Il tuo JSON — inclusi token, ID e dati dei clienti — non lascia mai la pagina e non viene mai inviato a un server. Funziona persino offline una volta che la pagina è stata caricata. Quando sei pronto, prova il convertitore da JSON a Python sul tuo payload.