Apariencia
📙 Clase 20 — JSON: guardar datos estructurados
Fase 4 · Archivos y el mundo exterior ⬅️ Volver al índice de clases
🎯 Qué aprendí
- JSON: el formato universal para guardar/intercambiar datos con estructura.
json.dump/json.load(a archivo) yjson.dumps/json.loads(a texto).- El hito del curso: mi To-Do por fin recuerda las tareas al cerrar. 🎉
📖 PARTE TEÓRICA
🌐 1. ¿Qué es JSON?
JSON (JavaScript Object Notation) es texto con una estructura que casi calca la de Python. Es EL formato de configuraciones, APIs y persistencia ligera.
| Python | JSON |
|---|---|
dict {"a": 1} | {"a": 1} |
list [1, 2] | [1, 2] |
str "hola" | "hola" (solo comillas dobles) |
int/float | número |
True / False | true / false (minúscula) |
None | null |
⚠️ Diferencias que delatan a un JSON: comillas dobles obligatorias,
true/false/nullen minúscula, y no admite comentarios ni comas finales.
💾 2. Guardar y cargar (archivo): dump / load
Tu "tabla en memoria" (lista de dicts, Clase 04) viaja al disco en dos líneas:
python
import json
tareas = [
{"texto": "Comprar pan", "hecha": False},
{"texto": "Estudiar Python", "hecha": True},
]
# GUARDAR: dump = "volcar" al archivo
with open("tareas.json", "w", encoding="utf-8") as f:
json.dump(tareas, f, ensure_ascii=False, indent=2)
# CARGAR: load = leer y reconstruir las estructuras
with open("tareas.json", encoding="utf-8") as f:
cargadas = json.load(f)
print(cargadas == tareas) # True ← ida y vuelta perfecta
print(type(cargadas).__name__) # list (¡vuelve como lista de dicts!)El archivo generado (mírala: es tu estructura, legible):
json
[
{
"texto": "Comprar pan",
"hecha": false
},
{
"texto": "Estudiar Python",
"hecha": true
}
]📌 Los dos parámetros que siempre querrás:
ensure_ascii=False(guarda "ñ/á" tal cual, noñ) eindent=2(formato legible para humanos).
🔤 3. A texto y de vuelta: dumps / loads
La versión con s (string) no toca archivos — convierte a/desde texto. La usarás con APIs:
python
s = json.dumps({"nombre": "Ana", "edad": 30}, ensure_ascii=False)
print(s) # {"nombre": "Ana", "edad": 30} ← un string
print(json.loads(s)) # {'nombre': 'Ana', 'edad': 30} ← de vuelta a dict🧪 Tip de entrevista: "¿
dumpvsdumps?" → Sin s: escribe a archivo. Con s: devuelve string. Igual paraload/loadsal leer.
🖥️ EN TU APP DE ESCRITORIO — el reto del Ejercicio 2, resuelto
Este es el "reto extra" de tu To-Do: que las tareas no se pierdan al cerrar. El patrón completo cargar/guardar con JSON:
python
import json
class App(ctk.CTk):
ARCHIVO = "tareas.json"
def __init__(self):
super().__init__()
self._tareas = self._cargar() # al abrir: restaurar
def _cargar(self):
try:
with open(self.ARCHIVO, encoding="utf-8") as f:
return json.load(f) # lista de dicts, lista p/ usar
except FileNotFoundError:
return [] # primer arranque
except json.JSONDecodeError:
return [] # archivo corrupto: no morir
def _guardar(self):
with open(self.ARCHIVO, "w", encoding="utf-8") as f:
json.dump(self._tareas, f, ensure_ascii=False, indent=2)
def _agregar(self):
self._tareas.append({"texto": self.entry.get(), "hecha": False})
self._guardar() # persistir en cada cambio
self._pintar()¿Y si tu modelo son objetos Tarea (Fase 3)? Conviertes al guardar y al cargar:
python
def _guardar(self):
datos = [{"texto": t.texto, "hecha": t.hecha} for t in self._tareas]
...
def _cargar(self):
...
return [Tarea(d["texto"], d["hecha"]) for d in json.load(f)]⚠️
jsonsolo serializa tipos básicos (dict/list/str/num/bool/None). Un objetoTareadirecto lanzaTypeError: Object of type Tarea is not JSON serializable— por eso el paso objeto → dict. (¿Notas el patrón? Es ela_dict()de la Clase 13.)
Otro uso inmediato: configuración de tu app (tema, tamaño, idioma) en un config.json.
🗄️ CON BASE DE DATOS (caso de uso)
JSON es tu peldaño previo a SQLite — y su compañero después:
| JSON | SQLite (Fase 6) | |
|---|---|---|
| Ideal para | config, listas pequeñas (< miles) | muchos registros, búsquedas, filtros |
| Buscar/filtrar | cargas TODO y filtras en Python | la BD filtra por ti (WHERE) |
| Modificar un registro | reescribes el archivo entero | UPDATE ... WHERE id=? |
🗄️ Además, las APIs hablan JSON: cuando en la Clase 23 consumas una API, la respuesta será JSON que
requestsconvierte a dicts — todo esto se reusa.
🏋️ EJERCICIOS CON SOLUCIÓN
Ejercicio 1 — Config de la app
Guarda {"tema": "oscuro", "fuente": 14} en config.json y vuelve a cargarlo.
Ver solución
python
import json
config = {"tema": "oscuro", "fuente": 14}
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)
with open("config.json", encoding="utf-8") as f:
cargada = json.load(f)
print(cargada["tema"]) # oscuroEjercicio 2 — Carga a prueba de fallos
Escribe cargar_lista(ruta) que devuelva la lista del JSON, o [] si el archivo no existe o está corrupto.
Ver solución
python
import json
def cargar_lista(ruta):
try:
with open(ruta, encoding="utf-8") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
print(cargar_lista("tareas.json")) # la lista guardada
print(cargar_lista("nada.json")) # []Ejercicio 3 — Objeto ↔ JSON
Con class Nota: __init__(self, titulo, texto), guarda una lista de notas en JSON y recárgala como objetos.
Ver solución
python
import json
class Nota:
def __init__(self, titulo, texto):
self.titulo = titulo
self.texto = texto
notas = [Nota("Compra", "pan y leche"), Nota("Idea", "app de gastos")]
with open("notas.json", "w", encoding="utf-8") as f:
json.dump([{"titulo": n.titulo, "texto": n.texto} for n in notas],
f, ensure_ascii=False, indent=2)
with open("notas.json", encoding="utf-8") as f:
recuperadas = [Nota(d["titulo"], d["texto"]) for d in json.load(f)]
print(recuperadas[0].titulo) # Compra❓ Preguntas y respuestas (autoevaluación)
1. ¿Qué estructura de Python calca casi 1:1 un JSON?
Dicts y listas anidados (con str/num/bool/None como valores).
2. ¿json.dump vs json.dumps?
dumpescribe a un archivo;dumpsdevuelve un string.
3. ¿Para qué sirven ensure_ascii=False e indent=2?
Guardar acentos/ñ legibles (no
ñ) y formatear el archivo para humanos.
4. ¿Qué pasa si le pasas un objeto Tarea directo a json.dump?
TypeError: not JSON serializable. Hay que convertirlo antes a dict (y al cargar, de dict a objeto).
📎 Apuntes relacionados
- La lista de dicts que estás guardando → Clase 04
withy modos de archivo → Clase 19- El reto original → Ejercicio 2 · To-Do
➡️ Siguiente
Clase 21 · CSV — exportar tus datos a Excel.