Um tutorial detalha a construção, em Python, de um processo de serialização JSON capaz de lidar com tipos de dados personalizados, tolerar dados malformados e adaptar-se à evolução dos esquemas. O objetivo é fazer circular conjuntos de dados entre ficheiros, processos, microserviços e API sem que o pipeline seja interrompido ao primeiro sinal de anomalia.
De acordo com o artigo, apenas uma pequena parte do tempo de um engenheiro de machine learning é dedicada à escrita da arquitetura do modelo. O resto é dedicado à canalização de dados («data plumbing»), ou seja, à limpeza, pré-processamento e transformação dos dados. Uma parte deste trabalho consiste em serializar os dados, convertê-los em texto para armazenamento ou transmissão, e depois desserializá-los, ou seja, reconstruí-los em objetos Python.
O problema inicial reside nas limitações do formato JSON, que apenas reconhece alguns tipos básicos. Um dicionário de configuração de experiência que contenha um Enum, um set, um Path ou um datetime provoca um erro logo na chamada a json.dumps(), com a mensagem «TypeError: Object of type ModelType is not JSON serializable».
Um codificador personalizado
O primeiro passo consiste em definir uma classe que herda de json.JSONEncoder e redefine o seu método default(). Para cada tipo problemático, uma condição do tipo isinstance() indica a conversão a realizar. Os datetime são assim transformados num dicionário contendo um valor no formato ISO, os set em listas, os Path em cadeias de caracteres e os Enum no seu valor.
Um detalhe desempenha um papel central: o codificador adiciona uma chave «__type__» aos objetos que podem perder a sua natureza original, como um set convertido numa lista. Uma vez passado a json.dumps() através do argumento cls, o dicionário de exemplo é serializado sem erro, e o datetime aparece sob a forma «2026-09-28T14:26:10.328310».
Um descodificador tolerante a erros
No sentido inverso, a função robust_decoder é transmitida a json.loads() através do argumento object_hook. É chamada em cada dicionário aninhado e reconstrói o objeto original quando a chave «__type__» está presente. As conversões são colocadas num bloco try, que intercepta os erros ValueError e TypeError.
Em caso de dados malformados, a função regista um aviso através do módulo logging e devolve o valor bruto, sem interromper o processamento. O artigo especifica que este comportamento pode ser substituído por um valor de substituição ou por um dicionário de erro específico. Após a desserialização, a data é corretamente reconhecida como um datetime e a lista como um set.
Gerir a evolução dos esquemas
Os esquemas de metadados dos modelos mudam frequentemente: hiperparâmetros são adicionados, outros são abandonados, algumas métricas são renomeadas. Para responder a isto, o autor propõe uma função de fábrica, load_experiment_config(), que carrega o JSON com o descodificador e depois aplica valores por defeito através de get().
O exemplo refere-se a um hiperparâmetro «learning_rate» ausente da configuração antiga, ao qual é atribuído o valor por defeito 0.001. O mesmo princípio pode ser aplicado a qualquer novo parâmetro. O artigo menciona também a possibilidade de eliminar campos obsoletos durante o carregamento. Aplicado à configuração original, o carregamento restitui os tipos esperados: um inteiro para o identificador, um set para as variáveis, um PosixPath para o caminho, um datetime para a data/hora e um float para a taxa de aprendizagem.
