Un tutorial dettaglia la costruzione, in Python, di un processo di serializzazione JSON in grado di gestire tipi di dati personalizzati, tollerare dati malformati e adattarsi all’evoluzione degli schemi. L’obiettivo è far circolare dataset tra file, processi, microservizi e API senza che la pipeline si interrompa alla prima anomalia.
Secondo l’articolo, solo una piccola parte del tempo di un ingegnere di machine learning è dedicata alla scrittura dell’architettura del modello. Il resto riguarda la ‘plomberia dei dati’ (« data plumbing »), ovvero la pulizia, la pre-elaborazione e la trasformazione dei dati. Una parte di questo lavoro consiste nel serializzare i dati, convertirli in testo per archiviarli o trasmetterli, e poi deserializzarli, cioè ricostruirli in oggetti Python.
Il problema di partenza risiede nei limiti del formato JSON, che conosce solo pochi tipi di base. Un dizionario di configurazione di un esperimento contenente un Enum, un set, un Path o un datetime provoca un errore alla chiamata di json.dumps(), con il messaggio « TypeError: Object of type ModelType is not JSON serializable ».
Un codificatore personalizzato
Il primo passo consiste nel definire una classe che eredita da json.JSONEncoder e ridefinisce il suo metodo default(). Per ogni tipo problematico, una condizione della forma isinstance() indica la conversione da effettuare. I datetime vengono così trasformati in un dizionario contenente un valore in formato ISO, i set in liste, i Path in stringhe di caratteri e gli Enum nel loro valore.
Un dettaglio gioca un ruolo centrale: il codificatore aggiunge una chiave « __type__ » agli oggetti che rischiano di perdere la loro natura originale, come un set convertito in lista. Una volta passato a json.dumps() tramite l’argomento cls, il dizionario di esempio viene serializzato senza errori, e il datetime appare nella forma « 2026-09-28T14:26:10.328310 ».
Un decodificatore tollerante agli errori
Nella direzione inversa, la funzione robust_decoder viene passata a json.loads() tramite l’argomento object_hook. Viene chiamata su ogni dizionario annidato e ricostruisce l’oggetto originale quando la chiave « __type__ » è presente. Le conversioni sono poste in un blocco try, che intercetta gli errori ValueError e TypeError.
In caso di dato malformato, la funzione registra un avvertimento tramite il modulo logging e restituisce il valore grezzo, senza interrompere l’elaborazione. L’articolo precisa che questo comportamento può essere sostituito da un valore di fallback o da un dizionario di errore specifico. Dopo la deserializzazione, la data è correttamente riconosciuta come datetime e la lista come set.
Gestire l’evoluzione degli schemi
Gli schemi di metadati dei modelli cambiano spesso: vengono aggiunti iperparametri, altri vengono abbandonati, alcune metriche rinominate. Per rispondere a ciò, l’autore propone una funzione factory, load_experiment_config(), che carica il JSON con il decodificatore e poi applica valori predefiniti utilizzando get().
L’esempio riguarda un iperparametro « learning_rate » assente dalla vecchia configurazione, al quale viene assegnato il valore predefinito 0.001. Lo stesso principio può essere applicato a qualsiasi nuovo parametro. L’articolo menziona anche la possibilità di eliminare campi obsoleti durante il caricamento. Applicato alla configurazione originale, il caricamento restituisce i tipi attesi: un intero per l’identificativo, un set per le variabili, un PosixPath per il percorso, un datetime per il timestamp e un float per il tasso di apprendimento.
