Cómo preservar valores nulos al volcar YAML con ruamel.yaml

Instalación de la dependencia

Primero, es necesario intsalar la biblioteca ruamel.yaml mediante pip:

pip install ruamel.yaml

Implementación del método personalizado

El mecanismo principal consiste en sobrescribir el método represent_none del volizador (Dumper). A continuación se presenta una implementación completa:

import ruamel.yaml

# Datos de ejemplo con diversos tipos, incluyendo valores nulos
configuracion = {
    "titulo": "Aplicación Demo",
    "descripcion": None,
    "activo": True,
    "etiquetas": ["dev", None, "python"],
    "parametros": {
        "timeout": None,
        "max_conexiones": 100
    }
}

# Crear una instancia personalizada del Dumper
class DumperPersonalizado(yaml.Dumper):
    pass

# Definir cómo representar valores None
DumperPersonalizado.add_representer(
    type(None),
    lambda dumper, value: dumper.represent_scalar('tag:yaml.org,2002:null', '')
)

# Volcar los datos al archivo YAML
with open('configuracion.yaml', 'w', encoding='utf-8') as archivo:
    yaml.dump(configuracion, archivo, Dumper=DumperPersonalizado, allow_unicode=True)

Alternativa usando represent_none directamente

Otra aproximación más directa consiste en modificar la instancia del Dumper después de crearla:

import ruamel.yaml

datos_ejemplo = {
    "usuario": "admin",
    "email": None,
    "roles": [None, "editor", None]
}

# Instanciar el Dumper con opciones personalizadas
volizador = yaml.Dumper(width=120)
volizador.represent_none = lambda self, data: self.represent_scalar(
    'tag:yaml.org,2002:null', 
    ''
)

# Generar la salida YAML como cadena
resultado = yaml.dump(datos_ejemplo, Dumper=volizador)
print(resultado)

Resultado esperado

Al ejecutar el código anterior, el archivo YAML generado presentará el siguiente formato:

titulo: Aplicación Demo
descripcion: ''
activo: true
etiquetas:
- dev
- ''
- python
parametros:
  timeout: ''
  max_conexiones: 100

Verificación mediante pruebas

Para validar el comportamiento correcto, se puede implementar una prueba unitaria:

import ruamel.yaml
import pytest

class DumperPruebas(yaml.Dumper):
    pass

DumperPruebas.add_representer(
    type(None),
    lambda d, v: d.represent_scalar('tag:yaml.org,2002:null', '')
)

def verificar_yaml_vacio():
    datos = {
        "nombre": "Prueba",
        "valor": None,
        "habilitado": False,
        "elementos": [1, None, 3]
    }
    
    salida = yaml.dump(datos, Dumper=DumperPruebas)
    assert "valor: ''" in salida, "El valor nulo no se representó correctamente"
    assert "- ''" in salida, "El valor nulo en lista no se representó correctamente"
    print("Todas las verificaciones pasaron exitosamente")

if __name__ == "__main__":
    verificar_yaml_vacio()

Casos de uso en proycetos reales

Esta técnica resulta particularmente útil en escenarios donde se requiere mantener una distinción clara entre valores nulos y valores ausentes. Por ejemplo, en archivos de configuración de aplicaciones, en definiciones de esquemas de API, o cuando se procesan datos para modelos de machine learning donde los valores nulos tienen un significado específico distinto de los valores por defecto.

Publicado el 7-12 06:09