JSON5 y JSONC: JSON con comentarios
En resumen: Ambos dialectos existen porque la configuración escrita por humanos necesita comentarios y comas finales, pero su sintaxis y alcance en el ecosistema difieren, así que elegir mal atasca tu cadena de herramientas.

En resumen: usa JSONC para comentarios dentro de la configuración de VS Code y TypeScript, usa JSON5 cuando necesites la sintaxis de literales más amplia de ES5.1, como comillas simples o claves sin comillas, y mantén las API públicas en JSON estándar.
El JSON estándar no permite comentarios, así que todos los equipos los escriben en silencio de todos modos, hasta que un día el build se rompe. En lugar de hacerlo a escondidas, elige un dialecto adecuado.
1. Los dos dialectos tienen motivaciones distintas
- JSONC (JSON with Comments): JSON estándar más comentarios y comas finales, sin apenas otros cambios de sintaxis. Es el estándar de facto del ecosistema de VS Code:
settings.json,tsconfig.jsony.eslintrc.jsonlo usan todos. - JSON5: mucho más ambicioso, alineado directamente con el literal de objeto de ES5.1: comillas simples, claves sin comillas, hexadecimal,
Infinity/NaN, continuaciones de línea con barra invertida y signos+iniciales.
Una frase para distinguirlos: JSONC solo hace más cómodo escribir; JSON5 te deja escribir un archivo de configuración como si fuera JavaScript.
2. Tabla comparativa de sintaxis
| Característica | JSON estándar | JSONC | JSON5 |
|---|---|---|---|
Comentarios // /* */ |
✗ | ✓ | ✓ |
| Comas finales | ✗ | ✓ | ✓ |
| Cadenas con comillas simples | ✗ | ✗ | ✓ |
| Claves sin comillas | ✗ | ✗ | ✓ |
| Números hexadecimales | ✗ | ✗ | ✓ |
Punto decimal inicial/final .5 5. |
✗ | ✗ | ✓ |
Números que empiezan por + |
✗ | ✗ | ✓ |
Infinity / NaN |
✗ | ✗ | ✓ |
Continuación de fin de línea \ |
✗ | ✗ | ✓ |
| Cadenas multilínea | ✗ | ✗ | ✓ (implementadas mediante continuación) |
| Valor arbitrario en el nivel superior | ✓ | ✓ | ✓ |
La especificación completa de JSON5 apenas ocupa más de una página; el paquete json5 lleva mucho tiempo mantenido por la comunidad, y los navegadores y Node se comportan de forma idéntica.
3. Parseo: dos APIs, dos tipos de capacidad de posicionamiento
3.1 JSON5: parseo sencillo de todo el documento
import JSON5 from 'json5';
const config = JSON5.parse(`
{
// puerto del servicio
port: 8080,
host: '0.0.0.0',
features: ['json5', 'jsonc',],
retries: 0x10,
}
`);
console.log(config.port, config.retries); // 8080 16
JSON5.parse devuelve un objeto plano sin información de offsets, porque no está orientado a escenarios de edición, solo a "leer configuración".
3.2 JSONC: un árbol sintáctico con offsets
El valor de jsonc-parser no está en el parseo sino en la edición. Ofrece tres capas de API:
| API | Propósito | Uso típico |
|---|---|---|
parse |
Parsear a un árbol que lleva offset/length |
Resaltado de sintaxis, plegado, navegación |
visit |
Recorrido en profundidad; el callback recibe la ruta del nodo | Inspección tipo JSONPath |
modify + applyEdits |
Producir una edición de texto mínima y aplicarla al texto original | Cambiar un valor conservando los comentarios |
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';
const text = `{
// configuración del puerto
"server": {
"port": 8080 // 80 en producción
}
}`;
const tree = parseTree(text);
const portNode = findNodeAtLocation(tree!, ['server', 'port']);
console.log(portNode?.offset, portNode?.length);
const edits = modify(text, ['server', 'port'], 3000, {
formattingOptions: { insertSpaces: true, tabSize: 2 },
});
console.log(applyEdits(text, edits));
La salida conserva los comentarios originales y reemplaza solo los tres caracteres de 8080:
{
// configuración del puerto
"server": {
"port": 3000 // 80 en producción
}
}
Eso es exactamente lo que "editar configuración con expresiones regulares" no puede hacer: una regex no tiene idea de qué 8080 cambiar, ni de que el 8080 dentro de una cadena debe dejarse intacto.
3.3 Los mensajes de error deben llevar offsets
Los códigos de error de jsonc-parser son valores de enum; los comunes son:
| Código de error | Significado |
|---|---|
InvalidSymbol |
Apareció un carácter que no debería estar ahí |
PropertyNameExpected |
Se esperaba un nombre de clave |
ColonExpected |
Se esperaban dos puntos |
CommaExpected |
Se esperaba una coma |
CloseBraceExpected |
No se cerró el objeto |
UnexpectedEndOfComment |
No se cerró el comentario |
InvalidEscapeCharacter |
Escape inválido |
Una vez que offset se convierte en línea y columna, el editor puede dibujar el subrayado ondulado exactamente sobre la posición infractora.
4. Cuándo usar cada uno
| Escenario | Recomendación | Razón |
|---|---|---|
| Cuerpos de petición/respuesta de API públicas | JSON estándar | Los clientes de propósito general no soportan dialectos |
package.json, composer.json |
JSON estándar | Los gestores de paquetes solo entienden JSON estándar |
tsconfig.json, .vscode/settings.json |
JSONC | Valor predeterminado del ecosistema, soportado de forma nativa por la cadena de herramientas |
| Configuración de aplicación (necesita comentarios) | JSONC o YAML | Hay que conservar los comentarios mientras las herramientas siguen pudiendo leer y escribir |
Necesitar Infinity, hexadecimal, literales complejos |
JSON5 | La mayor potencia expresiva |
| Intercambio de datos, logs, cachés | JSON delimitado por líneas o estándar | Se requiere streaming y universalidad |
Regla práctica: si un "herramienta no humana" va a leer el archivo, prefiere JSON estándar; considera un dialecto solo cuando estés seguro de que lo lee únicamente tu propio código.
5. Tres tipos de riesgo que introducen los dialectos
5.1 Desbordamiento del requisito
Cuando otra herramienta lee un archivo de configuración JSON5 y llama a JSON.parse, se cae directamente. El síntoma típico es "funciona bien en local, falla en Docker", porque el contenedor ejecuta una ruta de lectura distinta.
Contramedida: añade al repositorio una comprobación de CI que enumere explícitamente qué archivos pueden usar un dialecto; todo lo demás se valida como JSON estándar.
5.2 Sorpresas en la inferencia de tipos
JSON5 permite claves sin comillas y comillas simples, pero no te deja introducir construcciones literales como fechas o expresiones regulares; sigue siendo un formato de datos, no una evaluación de JavaScript.
Hay quien ha asumido que JSON5 podía expresar new Date() solo para ver cómo el parseo fallaba. JSON5 no es eval.
5.3 Comentarios descartados en silencio
Ni JSON5.stringify ni JSON.stringify conservan los comentarios. Así que "leerlo, retocar un valor y volver a escribirlo" pierde todos los comentarios.
Cuando los comentarios deben sobrevivir, las únicas opciones son la API de edición de jsonc-parser, o una biblioteca YAML que soporte la conservación de comentarios.
6. Recomendaciones prácticas
- [ ] Mantén una tabla "tipo de archivo → formato" en la documentación del equipo para que nadie improvise;
- [ ] Envuelve la lectura de configuración en un único
loadConfig()que decida internamente qué parser usar, para que el código de negocio nunca llame directamente aJSON.parse; - [ ] Cuando modifiques programáticamente una configuración con comentarios, usa
modify+applyEdits; nunca reescribas todo el archivo; - [ ] Valida también en CI los archivos de dialecto (
jsonc-parserpara JSONC,JSON5.parsepara JSON5), de lo contrario los errores de sintaxis solo aparecen en tiempo de ejecución; - [ ] Usa JSON estándar para interfaces públicas, claves de caché y payloads de colas de mensajes.
FAQ
¿Cómo debo elegir entre JSON5 y JSONC?
Elige por runtime en lugar de por cantidad de sintaxis: VS Code, TypeScript y ESLint leen JSONC de forma predeterminada, así que JSONC da la mejor compatibilidad; recurre a JSON5 solo cuando necesites la sintaxis de literales más amplia de ES5.1, como comillas simples, claves sin comillas o Infinity. Ninguno de los dos dialectos es apropiado para respuestas de API públicas.
¿Por qué un parser de JSON estándar no puede leer JSONC directamente?
JSONC permite comentarios y comas finales, mientras que JSON.parse solo acepta JSON de RFC 8259 y falla con cualquiera de los dos. Leer JSONC requiere un parser como jsonc-parser, o eliminar los comentarios antes de entregar el texto a un parser estándar.
¿Deberían sobrevivir los comentarios de JSONC a las ediciones del archivo?
Sí, pero solo mediante una API de edición. modify y applyEdits de jsonc-parser cambian el nodo objetivo conservando el resto de comentarios; parsear a un objeto y volver a stringificarlo descarta todos los comentarios.