Conservar datos estructurados con JSON

Ciencia y tecnologíaConservar datos estructurados con JSON

Guardar una lista de tareas como texto libre dificulta recuperar sus títulos y estados. JSON ofrece un formato de texto para representar datos estructurados. En esta lección guardarás una lista de diccionarios, la recuperarás y comprobarás su estructura. Aprenderás además por qué un archivo que se puede interpretar no tiene por qué contener datos adecuados para tu aplicación.

Un formato de datos, no un programa

JSON representa objetos, listas, cadenas, números, booleanos y un valor nulo. Se parece en algunos aspectos a los literales de Python pero no es idéntico. Por ejemplo, los booleanos se escriben true y false en JSON mientras que Python utiliza True y False. No hagas sustituciones manuales para convertir formatos.

El módulo json de la biblioteca estándar se encarga de la transformación. La operación de convertir una estructura a texto se llama serialización. Recuperar la estructura desde el texto se llama deserialización. No necesitas dominar esos términos para utilizar las funciones pero conviene reconocerlos en la documentación y en los mensajes técnicos.

No uses eval para leer un archivo de datos. JSON debe interpretarse como datos, no ejecutarse como instrucciones. Mantener esa separación evita que un formato pensado para almacenar tareas se convierta en un mecanismo para ejecutar código inesperado.

Convertir una lista a texto

Empieza con dos tareas inventadas. json.dumps devuelve una cadena JSON. El argumento ensure_ascii=False mantiene las tildes legibles y indent=2 añade sangría para facilitar la inspección humana. Estas opciones afectan a la representación, no al significado de los registros.

import json

tareas = [
    {"titulo": "Revisar programación", "hecha": False},
    {"titulo": "Preparar material", "hecha": True}
]
texto = json.dumps(tareas, ensure_ascii=False, indent=2)
print(texto)
recuperadas = json.loads(texto)
assert recuperadas == tareas

loads interpreta una cadena. No recibe una ruta para abrir por su cuenta. La s final de dumps y loads ayuda a recordar que aquí trabajamos con una representación de texto. También existen funciones para objetos de archivo pero en este curso combinaremos estas operaciones con Path para mantener cada paso visible.

Guardar y recuperar el documento

Utiliza un archivo llamado tareas-prueba.json junto al script. Es un nombre diferente del archivo del proyecto final para que las pruebas no interfieran con tus datos de trabajo. La extensión ayuda a identificar el formato pero no garantiza que el contenido sea correcto.

import json
from pathlib import Path

ruta = Path(__file__).resolve().parent / "tareas-prueba.json"
tareas = [{"titulo": "Practicar JSON", "hecha": False}]
texto = json.dumps(tareas, ensure_ascii=False, indent=2)
ruta.write_text(texto, encoding="utf-8")
contenido = ruta.read_text(encoding="utf-8")
recuperadas = json.loads(contenido)
print(recuperadas[0]["titulo"])

La última salida debe ser «Practicar JSON». Después de ejecutar, abre el archivo con un editor y observa la representación. No confundas la edición del JSON con la del código Python. Una coma mal colocada en cualquiera de los dos puede producir un error pero las reglas de cada formato no son exactamente las mismas.

Sintaxis válida y estructura válida

Un documento como 17 es JSON válido pero no representa la lista de tareas que espera nuestro programa. También sería válido un objeto con una clave distinta. Por tanto, después de interpretar el texto necesitas comprobar la estructura y los tipos que exige tu modelo.

Podemos definir una función que valide una lista de registros con titulo no vacío y hecha booleana. Utilizaremos type(…) is bool para exigir precisamente un booleano en ese campo. Cada rechazo devuelve False sin modificar los datos recibidos.

def datos_validos(datos):
    if not isinstance(datos, list):
        return False
    for tarea in datos:
        if not isinstance(tarea, dict):
            return False
        if set(tarea) != {"titulo", "hecha"}:
            return False
        if not isinstance(tarea["titulo"], str):
            return False
        if not tarea["titulo"].strip():
            return False
        if type(tarea["hecha"]) is not bool:
            return False
    return True

set(tarea) obtiene el conjunto de claves. La comparación exige exactamente los dos campos definidos. Es una decisión de este modelo pequeño. En una aplicación que evolucione quizá necesites admitir nuevos campos mediante una versión del formato. No cambies esa regla sin pensar cómo afectará a archivos guardados anteriormente.

Una lista vacía es válida: representa que no hay tareas. La función devuelve True tras recorrer cero elementos. Una tarea con título compuesto solo por espacios no lo es. Esta diferencia mantiene coherencia con la validación que utilizaremos al añadir tareas desde el menú.

Responder a un documento dañado

json.loads puede lanzar json.JSONDecodeError si el texto no tiene sintaxis JSON válida. La lectura puede producir OSError por problemas del sistema o UnicodeDecodeError si el contenido no corresponde a la codificación esperada. Son situaciones diferentes de que el archivo no exista en una primera ejecución.

La respuesta prudente del proyecto será detener la carga y conservar el archivo. No sustituiremos silenciosamente un documento dañado por una lista vacía que después se guarde encima. Mostrar un aviso y permitir revisar una copia protege mejor el trabajo que fingir que nunca hubo tareas.

Errores habituales

  • Creer que una extensión .json certifica que el contenido es válido.
  • Pasar una ruta a json.loads en lugar del texto leído.
  • Confundir JSON correcto con datos adecuados para el modelo.
  • Convertir manualmente True y False mediante reemplazos de texto.
  • Sobrescribir datos tras una carga fallida sin pedir una decisión consciente.

Lo que deberías recordar

  • JSON conserva estructuras de datos en un formato de texto.
  • Interpretar el formato y validar el modelo son pasos distintos.
  • Los errores de lectura no deberían borrar el archivo original.
  • Una lista vacía puede ser un estado válido del programa.

Ejercicio práctico

En la decimosexta hoja describe el modelo de tareas y realiza una ida y vuelta con dos registros, uno pendiente y otro terminado. Comprueba que los datos recuperados son iguales a los originales.

En copias separadas prueba tres alteraciones: una coma que rompe la sintaxis, un título vacío y un estado escrito como «False». Anota qué etapa debe rechazarlas. La primera afecta a la interpretación JSON y las otras a la validación del modelo. Conserva el archivo correcto sin modificar para utilizarlo como referencia.

Siguiente capítulo: Diseñar un gestor de tareas útil.

Volver al índice del curso de programación desde cero.

Últimos posts

Artículos más vistos

Horóscopo diario
Menú diario