Es frecuente tener que comparar datos en JSON: respuestas de una API, archivos de configuración, archivos de traducción. Pero si comparas dos JSON tal cual, como texto, muchas veces aparecen montones de diferencias aunque los datos sean los mismos. Este artículo explica por qué y cómo solucionarlo.
Por qué aparecen diferencias falsas
El estándar JSON (RFC 8259) define un objeto como «una colección no ordenada de pares nombre/valor». Es decir, {"a":1,"b":2} y {"b":2,"a":1} representan los mismos datos. Pero como texto el orden de las líneas es distinto, y diff lo cuenta como diferencia. Además, estas otras diferencias aparecen sin que cambien los datos:
- El ancho de la sangría (2 espacios, 4 espacios, tabuladores) y el JSON comprimido en una sola línea
- Si hay o no espacio después de los dos puntos (
"a":1y"a": 1) - La notación de los números (
1.0y1,1e2y100) - El escape de las cadenas (
"\u00e9"y"é")
Solución: analizar y volver a escribir con la misma forma
El método más fiable es analizar (parsear) los dos JSON para convertirlos en datos y volver a serializarlos con las mismas reglas.
- Analiza los dos textos como JSON. Si hay errores de sintaxis, corrígelos primero.
- Ordena de forma recursiva las claves de todos los objetos.
- Vuelve a escribirlos con la misma sangría (por ejemplo, 2 espacios).
- Compara el resultado línea a línea.
Por ejemplo, estos dos JSON solo se diferencian en el orden de las claves y en el orden del array.
{"name":"kim","age":30,"tags":["a","b"]}
{"age":30,"name":"kim","tags":["b","a"]}
Tras ordenarlos y reescribirlos, la diferencia en el orden de las claves desaparece y solo queda la diferencia real: el orden del array.
@@ -2,7 +2,7 @@ "age": 30, "name": "kim", "tags": [- "a",- "b"+ "b",+ "a" ] }
El orden de los arrays no se ordena
A diferencia de los objetos, en los arrays el orden tiene significado. ["a","b"] y ["b","a"] son datos distintos. Por eso, en principio, al normalizar no se ordenan los arrays. Aun así, si se trata de un array cuyo orden no importa en la práctica, como una lista de etiquetas, ordenarlo tú mismo antes de comparar hará que el resultado sea más fácil de leer. Lo correcto depende del significado de los datos.
Lo que cambia al analizar
El método de analizar y volver a escribir tiene efectos secundarios que conviene conocer.
- La notación de los números se unifica.
1.0y1pasan a ser1y se ven iguales. Casi siempre es lo deseable, pero si la notación en sí importa, compara también el texto original. - La precisión de los enteros grandes. JavaScript trata los números como coma flotante de 64 bits, así que no puede representar con exactitud enteros mayores que
Number.MAX_SAFE_INTEGER(2^53 − 1 = 9007199254740991). Por ejemplo,9007199254740993se convierte en9007199254740992al analizarlo. Ten cuidado con los JSON que guardan identificadores largos como números. - Claves duplicadas. RFC 8259 solo recomienda (SHOULD) que los nombres dentro de un objeto sean únicos, y cada implementación decide qué hacer con las claves duplicadas.
JSON.parsede JavaScript se queda con el último valor. - Comentarios y comas finales. Los comentarios como
// comentarioy las comas finales como[1, 2,]no son JSON estándar (los admiten formatos extendidos como JSON5 o JSONC). Un analizador estándar da error.
Cómo hacerlo desde la línea de comandos
Si tienes jq instalado, puedes imprimir con las claves ordenadas usando la opción -S (--sort-keys).
jq -S . before.json > a.json
jq -S . after.json > b.json
diff -u a.json b.json
Diferencia con JSON Patch
El resultado de diff es una lista de cambios por líneas pensada para que la lea una persona. En cambio, JSON Patch (RFC 6902) es un estándar que expresa los cambios mediante rutas dentro de la estructura JSON, como {"op":"replace","path":"/age","value":31}, y JSON Merge Patch (RFC 7396) consiste en superponer sobre el original un JSON que contiene solo las partes cambiadas. Si los cambios se intercambian entre programas, lo adecuado son estos estándares; si los revisa una persona, un diff de texto tras ordenar las claves.
Pruébalo en esta herramienta
Si activas Ordenar claves JSON en las opciones del comparador de textos, analiza ambos lados, ordena las claves, los reescribe con sangría de 2 espacios y luego los compara. No cambia el orden de los arrays y, si el análisis falla, indica en qué lado y en qué carácter está el error. También puedes arrastrar dos archivos .json a la vez. Para leer el resultado como patch, consulta Cómo leer un diff unificado.