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

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

    Posted by Kupi on 10 marzo, 2025 en 12:36 am

    Hola, buen día! Tengo un pequeño problema con un ejercicio del curso profesional de Python. Es el módulo 9.4, sólo me falta leer el .json en otro archivo y compararlo con el .pickle y deberían ser lo mismo. Al inicio me salió que no eran iguales y es que al ver mi .json vi que no se están guardando los datos como una instancia de la clase ‘Patients’ aún cuando paso por una función que en teoría debería poder serializar a .json. Menciono que los códigos funcionan sin errores, pero desconozco por qué no se guarda mi diccionario en el .json como instancias de la clase. De antemano muchas gracias por sus respuestas

    Kupi respondido 1 mes 2 Miembros · 4 Respuestas
  • 4 Respuestas
  • Angel Sánchez

    organizador
    10 marzo, 2025 en 12:55 am

    Hola 😀

    El problema es bastante común cuando trabajamos con diferentes formatos de almacenamiento de datos en Python.

    JSON y pickle funcionan de manera diferente:

    • JSON solo puede guardar tipos básicos (diccionarios, listas, textos, números), pero no sabe qué es una “clase” de Python.
    • Pickle es específico de Python y guarda toda la información de los objetos, incluyendo a qué clase pertenecen.

    Cuando guardas objetos de tu clase Patient en JSON, pierden su identidad como instancias de Patient y se convierten en simples diccionarios. Por eso, al compararlos con los objetos guardados en pickle, aparecen como diferentes.

    La solución es crear un sistema para que JSON pueda recordar que esos datos pertenecían a la clase Patient:

    # Cuando guardas en JSON, añade información sobre la clase

    def convertir_patient_a_json(obj):

    if isinstance(obj, Patient):

    return {

    "__class__": "Patient", # Marcador que indica la clase

    "id": obj.id,

    "name": obj.name,

    "surname": obj.surname,

    "age": obj.age,

    "diabetic": obj.diabetic

    }

    raise TypeError(f"No puedo convertir {type(obj)} a JSON")

    # Cuando lees del JSON, reconstruye los objetos Patient

    def reconstruir_patient_desde_json(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

    Al usar estas funciones con json.dump() y json.load(), podrás mantener la información de clase al guardar y recuperar datos en formato JSON, haciendo que sean más comparables con los datos guardados en pickle.

    Dime si todo quedó claro o puedo ayudarte en algo más 😀

    • Kupi

      Miembro
      10 marzo, 2025 en 5:16 pm

      Muchas gracias por su respuesta! :D.

      Vale, ya me queda más claro cómo funciona, pero aún tengo un detalle que aunque pase la función que comentó para que al guardar el archivo a JSON se mantengan las propiedades de la clase, no se guardan :c. Mi diccionario se ve así y el archivo JSON se ve así.

      • 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}")
        
        • Kupi

          Miembro
          10 marzo, 2025 en 5:34 pm

          Muchas gracias!!! Ya ha funcionado!! 🙂

Inicia sesión para responder.

Start of Discussion
0 de 0 respuestas Junio 2018
Ahora