Responder a: ¿Por qué los datos en JSON no coinciden con los de un archivo .pickle?

  • Angel Sánchez

    organizador
    10 marzo, 2025 en 5:22 pm

    Hola 😀

    1. En la Imagen 1 (JSON) tienes un formato donde cada paciente es una lista con valores ordenados:

      "1": [1, "Juan", "Perez", 42, "S1"]
      
    2. En la Imagen 2 (objetos originales) tienes instancias de la clase Patient:

      Patient(id=1, name=Juan, surname=Perez, age=42, diabetic=S1)
      

    El problema es que tu JSON no está guardando los nombres de los atributos, solo los valores en una lista. Esto hace que cuando lo cargas, no puedas reconstruir automáticamente los objetos Patient.

    Aquí hay una solución que debería funcionar mejor:

    import json
    import pickle
    from collections import namedtuple
    
    # Definición de la clase (similar a lo que muestras)
    Patient = namedtuple('_Patient', ['id', 'name', 'surname', 'age', 'diabetic'])
    
    # Función para convertir Patient a un formato que JSON entienda mejor
    def patient_encoder(obj):
        if isinstance(obj, Patient):
            # Convertimos a diccionario con nombres de atributos
            return {
                "__class__": "Patient",
                "id": obj.id,
                "name": obj.name,
                "surname": obj.surname,
                "age": obj.age,
                "diabetic": obj.diabetic
            }
        # Si es un diccionario con pacientes
        elif isinstance(obj, dict) and all(isinstance(k, str) for k in obj.keys()):
            result = {}
            for key, value in obj.items():
                if isinstance(value, Patient):
                    result[key] = patient_encoder(value)
                else:
                    result[key] = value
            return result
        raise TypeError(f"No se puede serializar objeto de tipo {type(obj)}")
    
    # Para guardar en JSON con formato adecuado
    with open("patients.json", "w") as f:
        json.dump(patients_dict, f, default=patient_encoder, indent=2)
    
    # Para reconstruir los objetos Patient al leer
    def patient_decoder(obj):
        if "__class__" in obj and obj["__class__"] == "Patient":
            return Patient(
                id=obj["id"],
                name=obj["name"], 
                surname=obj["surname"],
                age=obj["age"],
                diabetic=obj["diabetic"]
            )
        return obj
    
    # Para cargar desde JSON recuperando los objetos Patient
    with open("patients.json", "r") as f:
        patients_from_json = json.load(f, object_hook=patient_decoder)
    

    La clave está en cambiar el formato JSON para que guarde cada paciente como un objeto con atributos nombrados, en lugar de una lista. Con esto, al cargar el JSON podrás reconstruir los objetos Patient exactamente igual que los originales.

    Un último consejo: para comprobar si los datos cargados desde JSON y pickle son iguales, puedes usar una comparación así:

    # Comparar si son iguales (una vez cargados ambos)
    are_equal = all(patients_from_json[key] == patients_from_pickle[key] for key in patients_from_json)
    print(f"¿Son iguales? {are_equal}")