Parsear JSON en JavaScript es algo que harás cientos de veces como desarrollador web. Por suerte, JavaScript tiene soporte nativo — no hacen falta librerías. Pero hay más matices en JSON.parse() y JSON.stringify() de los que la documentación deja ver. Vamos a verlo bien.

JSON.parse() — Convertir un string en objeto

JSON.parse() toma un string con formato JSON y lo convierte en un valor JavaScript. Ese valor suele ser un objeto o array, pero también puede ser un string, número, booleano o null:

js
const jsonString = '{"name": "Alice", "age": 30, "active": true}';
const user = JSON.parse(jsonString);

console.log(user.name);    // "Alice"
console.log(user.age);     // 30
console.log(user.active);  // true
console.log(typeof user);  // "object"

Fíjate en que la entrada debe ser un string. Un error frecuente es intentar parsear un valor que ya es un objeto — JSON.parse({}) lanza un SyntaxError porque primero convierte el objeto a "[object Object]", que no es JSON válido.

Envuelve siempre JSON.parse() en un try/catch

Esto es lo que nadie te cuenta: JSON.parse() lanza un SyntaxError si la entrada es JSON inválido. Si estás parseando datos de una API, input de usuario o un archivo, deberías siempre manejar ese error. Una sola respuesta malformada puede tumbar tu app si no lo haces:

js
function safeParseJSON(str) {
  try {
    return { data: JSON.parse(str), error: null };
  } catch (err) {
    return { data: null, error: err.message };
  }
}

const { data, error } = safeParseJSON('{"name": "Alice"}');
if (error) {
  console.error('Invalid JSON:', error);
} else {
  console.log(data.name); // "Alice"
}

// Handles bad input gracefully
const result = safeParseJSON('not json at all');
console.log(result.error); // "Unexpected token 'o', "not json "... is not valid JSON"

Uso un wrapper como este en casi todos mis proyectos. Hace que el manejo de errores sea consistente y evita excepciones sin capturar que suban hasta tu UI.

JSON.stringify() — Convertir un objeto en string

JSON.stringify() hace lo contrario: convierte un valor JavaScript en un string JSON. Lo usarás al enviar datos a una API, guardar en localStorage o escribir en un archivo:

js
const user = {
  name: "Bob",
  age: 25,
  roles: ["admin", "editor"],
  password: "secret123" // we'll handle this later
};

// Basic usage
const jsonString = JSON.stringify(user);
// '{"name":"Bob","age":25,"roles":["admin","editor"],"password":"secret123"}'

// Pretty-printed (great for logs and file output)
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
// {
//   "name": "Bob",
//   "age": 25,
//   "roles": ["admin", "editor"],
//   "password": "secret123"
// }

El tercer argumento de JSON.stringify() es el nivel de indentación. Usar 2 o 4 te da una salida legible. El valor por defecto (sin tercer argumento) produce JSON compacto y minificado — mejor para transmisión por red.

Lo que stringify() descarta silenciosamente

Esto les pica a los desarrolladores con frecuencia. JSON.stringify() omite silenciosamente ciertos valores porque JSON no los soporta:

js
const data = {
  name: "Alice",
  greet: function() { return "hi"; },   // Functions → dropped
  undef: undefined,                      // undefined → dropped
  sym: Symbol("key"),                    // Symbols → dropped
  nan: NaN,                              // NaN → null
  inf: Infinity,                         // Infinity → null
  date: new Date("2024-01-15")           // Dates → ISO string
};

console.log(JSON.stringify(data, null, 2));
// {
//   "name": "Alice",
//   "nan": null,
//   "inf": null,
//   "date": "2024-01-15T00:00:00.000Z"
// }
// greet, undef, sym are GONE
Ojo: Si serializas un objeto con valores undefined y luego lo parseas, esas claves estarán completamente ausentes. Esto puede causar bugs sutiles si tu código comprueba obj.key === undefined más adelante.

Obtener JSON de una API — El patrón del mundo real

En la práctica, la mayoría del parseo JSON ocurre al obtener datos de una API. La Fetch API lo hace sencillo — response.json() se encarga del parseo por ti:

js
async function getUser(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const user = await response.json(); // parses JSON automatically
    return user;
  } catch (err) {
    console.error('Failed to fetch user:', err);
    return null;
  }
}

const user = await getUser(42);
if (user) {
  console.log(`Hello, ${user.name}!`);
}

response.json() es esencialmente JSON.parse(await response.text()). También lanza un error si el cuerpo de la respuesta no es JSON válido, así que vale la pena capturarlo por separado en código de producción.

Trabajar con datos JSON anidados

El JSON profundamente anidado es habitual en APIs reales. Así puedes navegarlo de forma segura sin que se rompa por claves inexistentes:

js
const apiResponse = {
  "user": {
    "profile": {
      "address": {
        "city": "Berlin"
      }
    }
  }
};

// Unsafe — throws if any level is undefined
const city1 = apiResponse.user.profile.address.city; // "Berlin"

// Safe with optional chaining (ES2020+) — returns undefined instead of throwing
const city2 = apiResponse?.user?.profile?.address?.city; // "Berlin"
const zip   = apiResponse?.user?.profile?.address?.zip;  // undefined (not a crash)

// With a fallback using nullish coalescing
const country = apiResponse?.user?.profile?.address?.country ?? "Unknown";

El encadenamiento opcional (?.) y la coalescencia nula (??) son características modernas de JavaScript — ambas parte de la especificación ECMAScript desde 2020 — que hacen que trabajar con estructuras JSON inciertas sea mucho más seguro. Úsalas sin miedo — están soportadas en todos los navegadores modernos y Node.js 14+.

Las funciones replacer y reviver

Tanto JSON.stringify() como JSON.parse() aceptan un segundo argumento que te permite personalizar la transformación. Son genuinamente útiles en escenarios reales:

js
// replacer: filter or transform values during stringify
const user = { name: "Alice", password: "s3cr3t", age: 30 };
const safe = JSON.stringify(user, ["name", "age"]); // only include these keys
// '{"name":"Alice","age":30}'  — password is excluded

// reviver: transform values during parse
const dateJson = '{"name": "Alice", "createdAt": "2024-01-15T09:30:00Z"}';
const parsed = JSON.parse(dateJson, (key, value) => {
  if (key === "createdAt") return new Date(value); // convert string to Date
  return value;
});
console.log(parsed.createdAt instanceof Date); // true
console.log(parsed.createdAt.getFullYear());   // 2024

El patrón reviver es especialmente útil para restaurar objetos Date después de un viaje de ida y vuelta por JSON, ya que JSON no tiene un tipo fecha nativo.

Herramientas útiles

Al trabajar con JSON en proyectos JavaScript, estas herramientas ahorran tiempo: JSON Formatter para formatear respuestas minificadas de APIs, JSON Validator para comprobar errores de sintaxis, JSON Path para consultar campos específicos de payloads grandes, y JSON Escape cuando necesitas embeber JSON en un string. Para más profundidad, la referencia MDN de JSON es excelente.

Conclusión

JSON.parse() y JSON.stringify() son las dos funciones que necesitas. Los hábitos clave que hay que adquirir: envolver siempre parse() en try/catch, usar encadenamiento opcional para el acceso anidado, y saber qué descarta silenciosamente stringify(). Domina esos tres puntos y manejarás el 99% de los escenarios JSON reales sin sorpresas.