← Todos los artículos

Cómo manejar JSON roto en JavaScript

El JSON real suele estar sucio: comas finales, comillas simples, literales de Python, vallas Markdown. Aprende patrones comunes, cómo escribir un helper de parseo seguro y cuándo recurrir a una librería de reparación dedicada.

Todo desarrollador JavaScript lo ha visto: JSON.parse() lanza un SyntaxError y tu aplicación crashea. El JSON vino de una API, un archivo de config o el portapapeles de un usuario —— y simplemente no parsea. En esta guía vamos a recorrer exactamente por qué pasa, cómo se ve el JSON roto en la práctica, y cómo manejarlo con gracia —— desde un simple try/catch hasta la reparación automática.

1. Por qué JSON.parse() falla

El estándar JSON (RFC 8259) es deliberadamente estricto. JSON.parse() lo implementa al pie de la letra —— cualquier desviación, por menor que sea, lanza un SyntaxError sin recuperación parcial:

try {
  const data = JSON.parse('{ name: "Ada" }'); // key sin comillas
} catch (err) {
  console.error(err.message);
  // SyntaxError: Expected property name or '}' in JSON at position 2
}

A diferencia de un navegador parseando HTML (que tiene un modelo de recuperación de errores integrado), el parser JSON es una puerta dura. El parseo estricto es una feature: fuerza a los productores a emitir datos limpios. Pero cuando no controlas la fuente de datos —— output de LLM, archivos de config editados a mano, APIs de terceros —— necesitas una estrategia para cuando esa puerta se cierra de golpe.

2. Los patrones más comunes de JSON roto

Antes de echar mano de una librería de reparación, ayuda reconocer con qué estás lidiando. Casi todo el JSON roto del mundo real cae en una de estas categorías:

Comillas simples en vez de dobles

// ❌ JSON inválido
{ 'name': 'Ada Lovelace' }

// ✅ Válido
{ "name": "Ada Lovelace" }

Los literales de objeto en JavaScript aceptan comillas simples; JSON no. Los desarrolladores copian un literal de objeto directamente en un contexto JSON y se topan con este error constantemente.

Comas finales

// ❌ JSON inválido
{
  "name": "Ada",
  "active": true,   // coma final
}

// ✅ Válido
{
  "name": "Ada",
  "active": true
}

JavaScript moderno permite comas finales en arrays y objetos. JSON no. Este es el error más común en archivos JSON editados a mano.

Keys de objeto sin comillas

// ❌ JSON inválido
{ name: "Ada", score: 98 }

// ✅ Válido
{ "name": "Ada", "score": 98 }

Los literales de objeto JavaScript no requieren keys entre comillas. JSON requiere que todas las keys sean strings entre comillas dobles, sin excepción.

Comentarios JavaScript

// ❌ JSON inválido
{
  // este es el registro de usuario
  "name": "Ada",
  /* agregado en 2024 */
  "active": true
}

JSON no tiene sintaxis de comentarios. A veces se agregan comentarios a archivos de config (JSONC —— JSON-con-Comentarios —— es una extensión popular), pero JSON.parse() los rechazará.

Strings multilínea

// ❌ JSON inválido
{
  "description": "line one
  line two"
}

// ✅ Válido —— escapar el newline
{
  "description": "line one\nline two"
}

Los valores de string en JSON deben estar en una sola línea. Los newlines literales dentro de un string no están permitidos; usa la secuencia de escape \n en su lugar.

NaN, Infinity y undefined

// ❌ No es JSON válido
{ "ratio": NaN, "limit": Infinity, "value": undefined }

// ✅ Alternativas válidas
{ "ratio": null, "limit": 1e308, "value": null }

Son valores JavaScript válidos pero no parte de la especificación JSON. JSON.stringify() los convierte silenciosamente (NaN e Infinity se vuelven null; las propiedades undefined se eliminan por completo), lo cual puede causar bugs sutiles de round-trip.

Literales Python

// ❌ Estilo Python —— JSON inválido
{ "active": True, "deleted": False, "value": None }

// ✅ JSON válido
{ "active": true, "deleted": false, "value": null }

JSON fue diseñado para interoperar entre múltiples lenguajes. Los True, False y None de Python con mayúscula inicial son una fuente frecuente de bugs de datos entre lenguajes.

3. «Reparar» y «parsear» no son lo mismo

Es importante distinguir entre dos operaciones fundamentalmente distintas:

  • Parseo estricto —— validar que la entrada sea exactamente JSON correcto, reportar la ubicación precisa de cualquier error, rechazar cualquier cosa que se desvíe. Úsalo cuando controlas el productor de datos y quieres imponer un contrato.
  • Parseo con reparación —— intentar recuperar un valor JSON válido de una entrada sintácticamente imperfecta usando un conjunto de reglas heurísticas. Úsalo cuando los datos vienen de una fuente no confiable o imprecisa (output de LLM, portapapeles de usuario, servicio legacy).

Un parser de reparación hace suposiciones. Si encuentra True asume que querías decir true. Si encuentra una coma final la elimina. Estas suposiciones casi siempre son correctas para los patrones listados arriba —— pero pueden enmascarar silenciosamente un documento genuinamente malformado donde el «fix» cambia el significado pretendido.

La herramienta correcta depende del contexto. En una pipeline de respuestas de API validadas por schema, quieres parseo estricto para que los datos malos salgan a la superficie de inmediato. En una herramienta para desarrolladores donde un humano está pegando JSON copiado de un mensaje de Slack, el parseo con reparación ahorra tiempo.

Librerías que vale la pena conocer

Dos paquetes npm hacen la mayor parte del trabajo pesado, con trade-offs complementarios:

  • jsonrepair —— un parser de reparación tolerante. Le pasas JSON malo y obtienes de vuelta un string válido en el mejor esfuerzo. Maneja los patrones de arriba (comillas simples, comas finales, keys sin comillas, comentarios, literales Python, fences de markdown, corchetes sin cerrar, input parcial). Síncrono, sin dependencias, pequeño —— ideal cuando tienes un documento completo en memoria.
  • clarinet —— un parser JSON streaming estilo SAX. Úsalo cuando el JSON llega en chunks (un stream, un archivo grande, una respuesta de LLM token por token) y necesitas reaccionar a medida que aparecen keys/valores en vez de esperar al documento completo. Es estricto con la sintaxis, así que combínalo con jsonrepair si la fuente no es confiable.

Regla práctica: reparar cuando tienes un blob pequeño y no confiable; stream cuando el documento es grande o llega progresivamente.

4. Capturando errores y dando feedback amigable a los usuarios

El patrón mínimo viable es un try/catch. Hazlo siempre —— nunca dejes que JSON.parse() lance sin capturar:

function safeParseJson(text) {
  try {
    return { ok: true, value: JSON.parse(text) };
  } catch (err) {
    return { ok: false, error: err.message };
  }
}

const result = safeParseJson(userInput);
if (!result.ok) {
  showError(`Could not parse JSON: ${result.error}`);
}

El mensaje de error de JSON.parse() varía según el motor de JavaScript. V8 (Node.js, Chrome) incluye una pista de posición:

// Mensaje de error de V8
"Expected ',' or '}' after property value in JSON at position 42"

// Firefox SpiderMonkey
"JSON.parse: expected ',' or '}' after property value in object
at line 3 column 5 of the JSON data"

La información de posición es útil pero específica de cada motor. Si necesitas un reporte confiable de línea/columna entre entornos, un parser de descenso recursivo custom puede emitir errores estructurados mucho más fáciles de mostrar a los usuarios.

5. Cuándo auto-reparar vs. pedir confirmación

No todas las reparaciones tienen el mismo riesgo. Aquí hay una heurística práctica:

  • Seguro de reparar automáticamente —— comas finales, normalización de espacios en blanco, conversión de estilo de comillas, keys sin comillas, comentarios JavaScript, literales boolean/null de Python. Son transformaciones mecánicas sin ambigüedad semántica.
  • Considerar consultar al usuario —— reparaciones estructurales como auto-cerrar un objeto o array sin cerrar ({"name": "Ada"{"name": "Ada"}), o quitar contenido (eliminar un comentario que parece intencional). La reparación probablemente sea correcta, pero el usuario podría querer verificar.
  • Siempre marcar, nunca arreglar en silencio —— coerciones de tipo como NaN → null cambian el valor del dato. Muéstrale al usuario qué cambió.

Una buena UI de reparación muestra un diff: la entrada original a la izquierda, la salida reparada a la derecha. El usuario puede confirmar que el arreglo es correcto antes de copiar o enviar el resultado.

6. La ventaja de privacidad del procesamiento JSON en navegador

Cuando procesas JSON localmente en el navegador —— usando el motor de JavaScript que ya está corriendo la página —— los datos nunca dejan la máquina del usuario. Esto importa más de lo que los desarrolladores suelen darse cuenta:

  • API keys y credenciales —— los desarrolladores pegan regularmente payloads JSON que contienen secretos. Una herramienta del lado servidor loguea cada request.
  • PII y datos de salud —— el cumplimiento de GDPR y HIPAA se vuelve mucho más simple cuando no hay transferencia de datos.
  • Datos corporativos —— muchas políticas de seguridad prohíben pegar datos internos en servicios web de terceros.

El procesamiento en navegador te da reparación JSON con riesgo cero: sin servidor, sin logs, sin retención de datos. El cómputo ocurre en un <textarea> y unos pocos kilobytes de JavaScript.

Preguntas frecuentes

¿Cómo manejo un error de JSON.parse en JavaScript?

Envuelve la llamada en try/catch y devuelve un resultado estructurado en vez de dejar que el SyntaxError se propague (ve el helper safeParseJson arriba). Nunca llames a JSON.parse() sobre input no confiable sin un guard.

¿Hay alguna forma de parsear JSON roto automáticamente?

Sí —— un parser de reparación recupera un valor válido de entrada imperfecta aplicando heurísticas (eliminar comas finales, convertir comillas simples, poner en minúsculas True). Úsalo para fuentes no confiables como output de LLM o texto pegado, no para respuestas de API validadas por contrato donde quieres que los datos malos salgan a la superficie.

¿Cuándo debería reparar JSON vs. rechazarlo?

Repara cuando un humano pegó JSON aproximado o un LLM lo produjo; rechaza (parseo estricto) cuando controlas al productor y quieres imponer un schema. Las coerciones de tipo como NaN → null siempre deberían marcarse, nunca aplicarse en silencio.

¿Cuál es la causa más común de JSON roto?

Tratar un literal de objeto JavaScript como JSON —— comillas simples, keys sin comillas y comas finales. Ve JSON vs objetos JavaScript y la referencia de errores de sintaxis en [object Object] y otros errores.

7. Pruébalo ya —— pega tu JSON roto

Si tienes un string JSON malformado que necesitas arreglar ahora mismo, JSON Fix maneja todos los patrones de arriba —— comillas simples, comas finales, keys sin comillas, literales Python, comentarios, fences de código markdown y más. Todo corre en tu navegador; no se envía nada a un servidor.