Ir al contenido principal

JSON: stringify y parse

Introducción

JavaScript Object Notation, conocido como JSON, es un formato de intercambio de datos ligero y fácil de leer tanto para humanos como para máquinas. Se ha convertido en uno de los formatos más populares para transmitir información entre aplicaciones, especialmente en entornos web. JSON se basa en la sintaxis de los objetos de JavaScript, pero es independiente del lenguaje, lo que lo hace ideal para la comunicación entre sistemas escritos en diferentes tecnologías.

En este artículo aprenderemos qué es exactamente JSON, cómo convertir datos de JavaScript a JSON y viceversa utilizando los métodos JSON.stringify() y JSON.parse(), y veremos ejemplos prácticos de estos procesos fundamentales para el desarrollo web moderno.

¿Qué es JSON?

JSON (JavaScript Object Notation) es un formato de texto sencillo para el intercambio de datos. Su simplicidad lo ha convertido en una alternativa popular a XML y otros formatos más complejos. JSON se basa en dos estructuras:

  • Una colección de pares nombre/valor (similar a un objeto en JavaScript)
  • Una lista ordenada de valores (similar a un array en JavaScript)

Ejemplo de un objeto JSON:

{
  "nombre": "María",
  "edad": 28,
  "esEstudiante": false,
  "intereses": ["programación", "fotografía", "viajes"],
  "direccion": {
    "calle": "Gran Vía",
    "numero": 42,
    "ciudad": "Madrid"
  }
}

Diferencias entre objetos JavaScript y JSON

Aunque JSON está basado en la sintaxis de objetos de JavaScript, existen importantes diferencias:

  1. Comillas en las claves: En JSON, los nombres de las propiedades deben estar entre comillas dobles. En JavaScript, las comillas son opcionales para identificadores simples.

  2. Valores permitidos: JSON solo admite estos tipos de datos:

    • Strings (entre comillas dobles)
    • Números
    • Objetos
    • Arrays
    • Booleanos (true o false)
    • null
  3. No soporta: En JSON no puedes usar:

    • Funciones
    • Fechas (se convierten a strings)
    • undefined
    • Símbolos
    • Objetos con métodos
  4. Comentarios: JSON no admite comentarios, a diferencia de JavaScript.

JSON.stringify(): Convertir a JSON

El método JSON.stringify() convierte un valor de JavaScript en una cadena de texto JSON.

Sintaxis básica:

JSON.stringify(valor, reemplazador, espacio)

Donde:

  • valor: El valor que se va a convertir a JSON
  • reemplazador (opcional): Una función o un array que altera el proceso de conversión
  • espacio (opcional): Mejora la legibilidad añadiendo indentación

Ejemplos básicos:

// Objeto simple
const persona = {
  nombre: "Carlos",
  edad: 35,
  activo: true
};

const personaJSON = JSON.stringify(persona);
console.log(personaJSON);
// Resultado: '{"nombre":"Carlos","edad":35,"activo":true}'

// Array
const colores = ["rojo", "verde", "azul"];
const coloresJSON = JSON.stringify(colores);
console.log(coloresJSON);
// Resultado: '["rojo","verde","azul"]'

// Valor simple
console.log(JSON.stringify(42));     // "42"
console.log(JSON.stringify("Hola")); // ""Hola""
console.log(JSON.stringify(true));   // "true"
console.log(JSON.stringify(null));   // "null"

Formateando el resultado con el parámetro de espacio:

const datos = {
  nombre: "Elena",
  cursos: ["JavaScript", "HTML", "CSS"],
  experiencia: {
    años: 3,
    nivel: "intermedio"
  }
};

// Sin formato
console.log(JSON.stringify(datos));

// Con 2 espacios de indentación
console.log(JSON.stringify(datos, null, 2));

// Con un carácter personalizado
console.log(JSON.stringify(datos, null, ".."));

El resultado con indentación hace que el JSON sea mucho más legible:

{
  "nombre": "Elena",
  "cursos": [
    "JavaScript",
    "HTML",
    "CSS"
  ],
  "experiencia": {
    "años": 3,
    "nivel": "intermedio"
  }
}

Usando el parámetro reemplazador:

Como array:
const usuario = {
  nombre: "Pedro",
  contraseña: "secreto123",
  email: "pedro@ejemplo.com",
  edad: 29
};

// Solo incluir estas propiedades
const usuarioFiltrado = JSON.stringify(usuario, ["nombre", "email"]);
console.log(usuarioFiltrado);
// Resultado: '{"nombre":"Pedro","email":"pedro@ejemplo.com"}'
Como función:
const datos = {
  nombre: "Ana",
  edad: 25,
  contraseña: "123456",
  fechaNacimiento: new Date(1997, 5, 12)
};

// Función para transformar valores
const reemplazador = (clave, valor) => {
  // Ocultar la contraseña
  if (clave === "contraseña") {
    return "******";
  }
  // Convertir fecha a formato legible
  if (valor instanceof Date) {
    return valor.toLocaleDateString();
  }
  return valor;
};

console.log(JSON.stringify(datos, reemplazador, 2));

Resultado:

{
  "nombre": "Ana",
  "edad": 25,
  "contraseña": "******",
  "fechaNacimiento": "12/6/1997"
}

Manejo de tipos especiales

JavaScript tiene tipos de datos que no existen en JSON. Veamos cómo se manejan:

const datosEspeciales = {
  funcion: function() { return "Hola"; },
  fecha: new Date(),
  regexp: /\d+/,
  infinito: Infinity,
  nan: NaN,
  indefinido: undefined,
  nulo: null,
  simbolo: Symbol("id")
};

console.log(JSON.stringify(datosEspeciales));
// Resultado: '{"fecha":"2023-10-15T10:30:00.000Z","regexp":{},"infinito":null,"nan":null,"nulo":null}'

Como puedes ver:

  • Las funciones se omiten
  • Las fechas se convierten a strings en formato ISO
  • Las expresiones regulares se convierten en objetos vacíos
  • Infinity y NaN se convierten en null
  • undefined y Symbol se omiten
  • null se mantiene como null

JSON.parse(): Convertir desde JSON

El método JSON.parse() analiza una cadena de texto JSON y la convierte en un objeto JavaScript.

Sintaxis básica:

JSON.parse(texto, revivificador)

Donde:

  • texto: La cadena JSON a analizar
  • revivificador (opcional): Función que transforma las propiedades y valores durante el proceso

Ejemplos básicos:

// Parsear un objeto
const jsonTexto = '{"nombre":"Laura","edad":31,"ciudad":"Barcelona"}';
const objeto = JSON.parse(jsonTexto);

console.log(objeto.nombre); // "Laura"
console.log(objeto.edad);   // 31

// Parsear un array
const jsonArray = '["manzana","naranja","plátano"]';
const frutas = JSON.parse(jsonArray);

console.log(frutas[1]); // "naranja"
console.log(frutas.length); // 3

// Parsear valores simples
console.log(JSON.parse('"texto"')); // "texto"
console.log(JSON.parse('42'));      // 42
console.log(JSON.parse('true'));    // true
console.log(JSON.parse('null'));    // null

Usando el parámetro revivificador:

El revivificador es una función que se ejecuta para cada par clave-valor en el objeto resultante, permitiéndonos transformar valores durante el proceso de análisis.

const jsonData = '{"nombre":"Alberto","nacimiento":"1990-05-15","activo":true}';

const revivificador = (clave, valor) => {
  // Convertir string de fecha a objeto Date
  if (clave === "nacimiento" && typeof valor === "string") {
    return new Date(valor);
  }
  return valor;
};

const persona = JSON.parse(jsonData, revivificador);

console.log(persona.nacimiento instanceof Date); // true
console.log(persona.nacimiento.getFullYear());   // 1990

Errores comunes y su resolución

1. JSON no válido

Si intentamos analizar una cadena que no es JSON válido, se producirá un error:

try {
  // JSON inválido: comillas simples, propiedad sin comillas
  const datos = JSON.parse('{nombre: "Juan"}');
} catch (error) {
  console.error("Error al parsear JSON:", error.message);
  // "Error al parsear JSON: Unexpected token n in JSON at position 1"
}

Solución: Asegúrate de que el JSON cumple con el formato correcto:

  • Propiedades entre comillas dobles
  • Valores string entre comillas dobles
  • Sin comas finales en arrays u objetos

2. Pérdida de tipos específicos

const original = {
  fecha: new Date(),
  regexp: /hola/,
  funcion: function() { return "Mundo"; }
};

const jsonStr = JSON.stringify(original);
const recuperado = JSON.parse(jsonStr);

console.log(recuperado.fecha); // String, no un objeto Date
console.log(typeof recuperado.fecha); // "string"
console.log(recuperado.regexp); // {}, no una expresión regular
console.log(recuperado.funcion); // undefined, la función se perdió

Solución: Usar la función revivificador para reconstruir tipos específicos:

const jsonConFecha = '{"nombre":"Teresa","fecha":"2023-10-15T12:00:00.000Z"}';

const recuperarTipos = (clave, valor) => {
  if (clave === "fecha" && typeof valor === "string") {
    return new Date(valor);
  }
  return valor;
};

const objetoConTipos = JSON.parse(jsonConFecha, recuperarTipos);
console.log(objetoConTipos.fecha instanceof Date); // true

Uso en almacenamiento y comunicación

LocalStorage

Uno de los usos más comunes de JSON.stringify y JSON.parse es guardar y recuperar datos en el almacenamiento local del navegador:

// Guardar datos
const preferenciasUsuario = {
  tema: "oscuro",
  tamañoFuente: "grande",
  notificaciones: true,
  ultimoAcceso: new Date()
};

// Convertir a string para guardar
localStorage.setItem("preferencias", JSON.stringify(preferenciasUsuario));

// Recuperar datos
const preferenciasGuardadas = localStorage.getItem("preferencias");
const preferencias = JSON.parse(preferenciasGuardadas, (clave, valor) => {
  if (clave === "ultimoAcceso") return new Date(valor);
  return valor;
});

console.log(preferencias.tema); // "oscuro"
console.log(preferencias.ultimoAcceso instanceof Date); // true

Comunicación con API REST

JSON es el formato estándar para la comunicación entre cliente y servidor en las API modernas:

// Enviar datos a un servidor
const nuevoProducto = {
  nombre: "Portátil",
  precio: 899.99,
  stock: 10,
  caracteristicas: ["SSD", "16GB RAM", "i7"]
};

fetch("https://api.ejemplo.com/productos", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify(nuevoProducto)
})
.then(respuesta => respuesta.json()) // Parse automático
.then(datos => {
  console.log("Producto creado con ID:", datos.id);
})
.catch(error => console.error("Error:", error));

Circulares y estructuras anidadas

Al trabajar con objetos que contienen referencias circulares, JSON.stringify() lanzará un error:

const objetoA = { nombre: "Objeto A" };
const objetoB = { nombre: "Objeto B" };

// Creamos una referencia circular
objetoA.referencia = objetoB;
objetoB.referencia = objetoA;

try {
  const jsonCircular = JSON.stringify(objetoA);
} catch (error) {
  console.error("Error:", error.message);
  // "TypeError: Converting circular structure to JSON"
}

Solución: Implementar un reemplazador personalizado para eliminar o transformar referencias circulares:

const manejadorCircular = () => {
  const visitados = new WeakSet();
  
  return (clave, valor) => {
    // Si es un objeto y no es null
    if (typeof valor === "object" && valor !== null) {
      // Si ya lo visitamos antes, es una referencia circular
      if (visitados.has(valor)) {
        return "[Referencia Circular]";
      }
      // Marcamos este objeto como visitado
      visitados.add(valor);
    }
    return valor;
  };
};

const objetoA = { nombre: "Objeto A" };
const objetoB = { nombre: "Objeto B" };
objetoA.referencia = objetoB;
objetoB.referencia = objetoA;

// Ahora podemos convertirlo a JSON sin errores
const jsonSeguro = JSON.stringify(objetoA, manejadorCircular(), 2);
console.log(jsonSeguro);

Resumen

JSON (JavaScript Object Notation) es un formato de intercambio de datos ligero, basado en la sintaxis de objetos de JavaScript, pero con algunas limitaciones. JavaScript nos proporciona dos métodos fundamentales para trabajar con JSON:

  • JSON.stringify(): Convierte datos de JavaScript a formato de texto JSON, con opciones para personalizar el resultado mediante reemplazadores y formato de salida.
  • JSON.parse(): Convierte una cadena JSON en un objeto JavaScript, permitiendo transformaciones durante el proceso mediante la función revivificador.

Estos métodos son esenciales para el almacenamiento de datos en el navegador y la comunicación con servicios web, siendo parte fundamental del desarrollo web moderno. Al utilizarlos, es importante tener en cuenta las limitaciones de JSON en cuanto a tipos de datos soportados y manejar adecuadamente casos especiales como las referencias circulares.