Saltar al contenido
jsonbeautifiers
Español

Comparar JSON

Compara dos documentos por estructura: reformatear o cambiar el orden de las claves no cuenta.

Original
Modificado

Nada de lo que pegues sale de tu navegador. La lista de permitidos de connect-src convierte eso en una garantía del navegador, no en una promesa. Compruébalo tú mismo

Esto compara dos documentos por estructura y no por líneas. Reformatea uno de ellos, ordena sus claves, cambia su indentación: el diff sigue vacío, porque nada de eso cambió los datos.

Un diff de texto sobre JSON es casi inútil justo por esa razón. Reserializa un documento con otra biblioteca y todas las líneas aparecerán como modificadas sin que nada haya cambiado de verdad.

Qué cuenta como diferencia

Cuatro tipos de cambio, cada uno reportado con un JSON Pointer a la ubicación exacta.

Añadido
Una clave o elemento presente a la derecha y no a la izquierda.
Eliminado
Presente a la izquierda y no a la derecha.
Modificado
Presente en ambos con un valor distinto del mismo tipo.
Cambio de tipo
Presente en ambos con un tipo distinto. Merece la pena separarlo, porque un número que ha pasado a ser cadena suele ser un fallo de serialización y no un cambio de datos.

Cómo se comparan los arrays, y por qué importa

Los arrays son la parte difícil de cualquier diff estructural, y no hay una única respuesta correcta.

Comparar posición a posición es correcto para una tupla de forma fija y catastrófico para una lista. Inserta un elemento al principio de un array de mil elementos y todas las posiciones posteriores se reportarán como modificadas, lo que entierra la única diferencia real bajo mil falsas.

Esta herramienta hace en su lugar una pasada de subsecuencia común más larga, así que una inserción se reporta como inserción. Eso cuesta un tiempo proporcional al producto de las dos longitudes, así que por encima de un umbral pasa a comparación posicional y lo dice en las notas. Nunca se degrada en silencio.

Cosas que a propósito no son diferencias

El orden de las claves
Los objetos JSON no tienen orden según la especificación. Dos documentos con los mismos miembros en distinto orden son el mismo documento.
Espacios e indentación
No significativos para un parser, así que invisibles aquí.
0 y -0
Distintos bajo Object.is pero idénticos como números JSON.

How to do this in code

Comparación estructural en código.

sh jq

La primera línea es la que hay que recordar. Ordenar las claves primero elimina casi todo el ruido de un diff de texto.

# Sort keys recursively, then diff the text. Cheap and
# surprisingly effective for small documents.
diff <(jq -S . a.json) <(jq -S . b.json)

# Keys present in one and not the other
jq -n --slurpfile a a.json --slurpfile b b.json \
  '($a[0] | paths) - ($b[0] | paths)'
py Python
from deepdiff import DeepDiff

diff = DeepDiff(a, b, ignore_order=True)
print(diff)

# Standard library only, for a shallow comparison
changed = {k for k in a.keys() | b.keys() if a.get(k) != b.get(k)}
js JavaScript

JSON Patch es el formato estándar para transmitir un diff de JSON, y sus rutas son punteros RFC 6901 como los que reporta esta herramienta.

import { compare } from 'fast-json-patch';

// RFC 6902 JSON Patch: a list of operations that turns a into b
const patch = compare(a, b);
// [{ op: 'replace', path: '/user/name', value: 'Priya' }]
go Go

go-cmp es la opción estándar en los tests de Go y produce una salida legible para estructuras anidadas.

import "github.com/google/go-cmp/cmp"

if d := cmp.Diff(a, b); d != "" {
    t.Errorf("mismatch (-want +got):\n%s", d)
}

Preguntas frecuentes

¿Por qué reordenar las claves no muestra ninguna diferencia?
Porque la RFC 8259 define un objeto como una colección sin orden. En la práctica todos los parsers conservan el orden de inserción, y los diffs que dependen de él están comprobando la serialización y no los datos.
¿Puedo ignorar campos como las marcas de tiempo?
Sí. Ignorar una ruta la saca por completo de la comparación, que es como se comparan dos respuestas de API que solo difieren en requestId y generatedAt.
¿Qué es un JSON Pointer?
La RFC 6901: una ruta separada por barras como /users/0/email. Dentro de una clave hay dos caracteres que necesitan escape: ~ pasa a ~0 y / pasa a ~1. Cada cambio reportado aquí lleva uno, así que puedes direccionar la ubicación mediante código.