Serialisatie robuuster maken met JSON en aangepaste typen

Een tutorial beschrijft in detail hoe je in Python een JSON-serialisatieproces bouwt dat aangepaste datatypen aankan, misvormde data tolereert en zich aanpast aan evoluerende schema’s. Het doel is datasets te laten circuleren tussen bestanden, processen, microservices en API’s zonder dat de pijplijn bij de eerste afwijking vastloopt.

Volgens het artikel besteedt een machine learning-engineer slechts een klein deel van zijn tijd aan het schrijven van de modelarchitectuur. De rest gaat naar de dataplumbing (« data plumbing »), dat wil zeggen het opschonen, voorbewerken en transformeren van data. Een deel van dit werk bestaat uit het serialiseren van data, het omzetten naar tekst voor opslag of verzending, en vervolgens het deserialiseren, ofwel het reconstrueren tot Python-objecten.

Het uitgangspunt is de beperking van het JSON-formaat, dat slechts enkele basistypen kent. Een experimentconfiguratiedictionary die een Enum, een set, een Path of een datetime bevat, veroorzaakt een fout bij de aanroep van json.dumps(), met de melding « TypeError: Object of type ModelType is not JSON serializable ».

Een aangepaste encoder

De eerste stap is het definiëren van een klasse die overerft van json.JSONEncoder en zijn methode default() overschrijft. Voor elk problematisch type geeft een conditie van het type isinstance() aan welke conversie moet worden uitgevoerd. Datetime-objecten worden zo omgezet in een dictionary met een waarde in ISO-formaat, sets in lijsten, Paths in strings en Enums in hun waarde.

Een detail speelt een centrale rol: de encoder voegt een sleutel « __type__ » toe aan objecten die hun oorspronkelijke aard kunnen verliezen, zoals een set die in een lijst wordt omgezet. Nadat deze via het argument cls aan json.dumps() is doorgegeven, wordt de voorbeelddictionary foutloos geserialiseerd en verschijnt de datetime als « 2026-09-28T14:26:10.328310 ».

Een fouttolerante decoder

In de omgekeerde richting wordt de functie robust_decoder via het argument object_hook doorgegeven aan json.loads(). Deze wordt aangeroepen voor elke geneste dictionary en reconstrueert het originele object wanneer de sleutel « __type__ » aanwezig is. De conversies worden in een try-blok geplaatst, dat ValueError- en TypeError-fouten opvangt.

Bij misvormde data logt de functie een waarschuwing via de logging-module en retourneert de ruwe waarde, zonder het proces te onderbreken. Het artikel preciseert dat dit gedrag kan worden vervangen door een fallback-waarde of een specifieke foutendictionary. Na deserialisatie wordt de datum correct herkend als een datetime en de lijst als een set.

Omgaan met evoluerende schema’s

De metadata-schema’s van modellen veranderen vaak: hyperparameters worden toegevoegd, andere vervallen, sommige metrieken worden hernoemd. Om hiermee om te gaan, stelt de auteur een fabrieksfunctie voor, load_experiment_config(), die de JSON laadt met de decoder en vervolgens standaardwaarden toepast met behulp van get().

Het voorbeeld gaat over een hyperparameter « learning_rate » die ontbreekt in de oude configuratie, waaraan de standaardwaarde 0.001 wordt toegekend. Hetzelfde principe kan worden toegepast op elke nieuwe parameter. Het artikel vermeldt ook de mogelijkheid om verouderde velden bij het laden te verwijderen. Toegepast op de originele configuratie levert het laden de verwachte typen op: een integer voor de identifier, een set voor de variabelen, een PosixPath voor het pad, een datetime voor de timestamp en een float voor het leerpercentage.