IF0100 - Programación OO II

Unidad 4: Manejo de Persistencia

Clase 1: Persistencia en Archivos JSON y CSV

Martes, 28 de abril de 2026 Semana 13 - Martes (120 minutos) 60 min Teoría 60 min Práctica

E5: Proyecto SQLAlchemy - Jueves 07/05/2026 (9 días)

Objetivos de Aprendizaje

Al finalizar esta clase, serás capaz de:

  • Leer y escribir archivos JSON (JavaScript Object Notation) con el módulo json
  • Leer y escribir archivos CSV (Comma Separated Values) con el módulo csv
  • Usar pathlib para manejo moderno de rutas
  • Implementar persistencia básica para el proyecto TaskFlow
  • Manejar excepciones en operaciones de archivo

Archivos JSON (20 min)

JSON (JavaScript Object Notation) es un formato ligero para intercambio de datos. Python lo soporta nativamente.

Escribir JSON

import json
from pathlib import Path

# Datos a guardar
usuario = {
    "id": 1,
    "username": "juan123",
    "email": "juan@example.com",
    "activo": True
}

# Escribir archivo JSON
archivo = Path("data/usuario.json")
archivo.parent.mkdir(exist_ok=True)  # Crear directorio si no existe

with open(archivo, "w", encoding="utf-8") as f:
    json.dump(usuario, f, indent=4, ensure_ascii=False)

print(f"Archivo guardado en {archivo}")

Leer JSON

import json
from pathlib import Path

archivo = Path("data/usuario.json")

with open(archivo, "r", encoding="utf-8") as f:
    datos = json.load(f)

print(datos["username"])  # "juan123"
print(datos["email"])     # "juan@example.com"

Lista de objetos JSON

usuarios = [
    {"id": 1, "username": "juan", "email": "juan@test.com"},
    {"id": 2, "username": "ana", "email": "ana@test.com"},
    {"id": 3, "username": "pedro", "email": "pedro@test.com"}
]

# Guardar lista
with open("data/usuarios.json", "w", encoding="utf-8") as f:
    json.dump(usuarios, f, indent=2, ensure_ascii=False)

# Leer lista
with open("data/usuarios.json", "r", encoding="utf-8") as f:
    usuarios_cargados = json.load(f)

for u in usuarios_cargados:
    print(f"@{u['username']} - {u['email']}")

Archivos CSV (20 min)

CSV (Comma Separated Values) es un formato tabular comun en hojas de calculo.

Escribir CSV

import csv
from pathlib import Path

tareas = [
    {"id": 1, "titulo": "Aprender Python", "completada": False},
    {"id": 2, "titulo": "Hacer proyecto", "completada": True},
    {"id": 3, "titulo": "Estudiar FastAPI", "completada": False}
]

archivo = Path("data/tareas.csv")

with open(archivo, "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["id", "titulo", "completada"])
    writer.writeheader()
    writer.writerows(tareas)

print(f"CSV guardado con {len(tareas)} tareas")

Leer CSV

import csv
from pathlib import Path

archivo = Path("data/tareas.csv")

with open(archivo, "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    tareas = list(reader)

for tarea in tareas:
    completada = "✅" if tarea["completada"] == "True" else "⬜"
    print(f"{completada} {tarea['titulo']}")
Importante: CSV guarda todo como texto. Necesitas convertir tipos manualmente.

Modulo pathlib (10 min)

pathlib es la forma moderna de manejar rutas en Python.

from pathlib import Path

# Ruta actual
actual = Path.cwd()
print(f"Directorio actual: {actual}")

# Crear rutas
data_dir = Path("data")
archivo = data_dir / "usuarios.json"

# Verificar existencia
if not data_dir.exists():
    data_dir.mkdir(parents=True, exist_ok=True)

# Informacion del archivo
if archivo.exists():
    print(f"Nombre: {archivo.name}")        # usuarios.json
    print(f"Extension: {archivo.suffix}")   # .json
    print(f"Padre: {archivo.parent}")       # data
    print(f"Tamaño: {archivo.stat().st_size} bytes")

# Listar archivos
for f in data_dir.glob("*.json"):
    print(f"Encontrado: {f}")

Ejercicio: Repositorio JSON (25 min)

Crear un repositorio que guarde usuarios en JSON:

# repositorio_usuario.py
import json
from pathlib import Path
from typing import Optional, List

class UsuarioRepositorioJSON:
    """Repositorio que persiste usuarios en archivo JSON."""
    
    def __init__(self, archivo: str = "data/usuarios.json"):
        self.archivo = Path(archivo)
        self._asegurar_archivo()
    
    def _asegurar_archivo(self):
        """Crea el archivo si no existe."""
        self.archivo.parent.mkdir(parents=True, exist_ok=True)
        if not self.archivo.exists():
            self._guardar_todos([])
    
    def _leer_todos(self) -> List[dict]:
        """Lee todos los usuarios del archivo."""
        with open(self.archivo, "r", encoding="utf-8") as f:
            return json.load(f)
    
    def _guardar_todos(self, usuarios: List[dict]):
        """Guarda todos los usuarios en el archivo."""
        with open(self.archivo, "w", encoding="utf-8") as f:
            json.dump(usuarios, f, indent=2, ensure_ascii=False)
    
    def crear(self, usuario: dict) -> dict:
        """Crea un nuevo usuario."""
        usuarios = self._leer_todos()
        nuevo_id = max([u["id"] for u in usuarios], default=0) + 1
        usuario["id"] = nuevo_id
        usuarios.append(usuario)
        self._guardar_todos(usuarios)
        return usuario
    
    def obtener_por_id(self, id: int) -> Optional[dict]:
        """Obtiene un usuario por su ID."""
        usuarios = self._leer_todos()
        for u in usuarios:
            if u["id"] == id:
                return u
        return None
    
    def obtener_todos(self) -> List[dict]:
        """Obtiene todos los usuarios."""
        return self._leer_todos()
    
    def actualizar(self, id: int, datos: dict) -> Optional[dict]:
        """Actualiza un usuario existente."""
        usuarios = self._leer_todos()
        for i, u in enumerate(usuarios):
            if u["id"] == id:
                usuarios[i].update(datos)
                usuarios[i]["id"] = id  # No cambiar ID
                self._guardar_todos(usuarios)
                return usuarios[i]
        return None
    
    def eliminar(self, id: int) -> bool:
        """Elimina un usuario."""
        usuarios = self._leer_todos()
        for i, u in enumerate(usuarios):
            if u["id"] == id:
                usuarios.pop(i)
                self._guardar_todos(usuarios)
                return True
        return False


# === PRUEBAS ===
if __name__ == "__main__":
    repo = UsuarioRepositorioJSON()
    
    # Crear usuarios
    u1 = repo.crear({"username": "juan", "email": "juan@test.com"})
    u2 = repo.crear({"username": "ana", "email": "ana@test.com"})
    print(f"Creados: {u1['id']}, {u2['id']}")
    
    # Listar
    print(f"Total: {len(repo.obtener_todos())} usuarios")
    
    # Actualizar
    repo.actualizar(u1["id"], {"email": "nuevo@test.com"})
    
    # Eliminar
    repo.eliminar(u2["id"])
    print(f"Despues de eliminar: {len(repo.obtener_todos())}")
E4 Laboratorio: Implementa un repositorio CSV para tareas con las mismas operaciones CRUD.

Resumen

  • json.dump()/json.load(): Escribir/leer JSON
  • csv.DictWriter/DictReader: Escribir/leer CSV
  • pathlib.Path: Manejo moderno de rutas
  • with open(): Context manager para archivos
  • encoding="utf-8": Siempre especificar encoding
← Anterior: Persistencia de Datos
Clase 16 de 25
Siguiente: SQLAlchemy Modelos →