Comparar objetos JSON: claves añadidas, eliminadas y cambiadas
Ponga lado a lado la versión antigua y la nueva de un registro de datos y obtenga solo las diferencias reales: en el ejemplo, 2 añadidos, 1 eliminado y 2 cambios, 5 en total, sin que cuenten los espacios ni el orden de las claves. Gratis en su navegador.
- Gratis
- Funciona en su navegador
- Sin registro
- Sus datos no salen de su equipo
Ejecutar — gratis
Corre en su navegador. Gratis y sin límite: sus datos no salen de esta página.
Esta herramienta compara valores ya interpretados. Los arrays conservan su orden y se comparan completos. La operación es determinista, no usa la red y rechaza arrays, valores primitivos o null como entradas de nivel superior.
Comparar los datos, no el texto
Una comparación de texto hace que una actualización pequeña parezca enorme, porque la sangría, el orden de las claves y otros detalles de formato distraen de los datos. Esta herramienta lee las dos versiones como datos, de modo que solo cuentan las diferencias reales: si el contenido es el mismo con otros espacios u otro orden, las tres listas salen vacías. Cuando una sección existe en ambos lados, la comparación entra en ella y apunta al dato más profundo que cambió, como /profile/active en el ejemplo. Las listas se comparan enteras: añadir editor a roles produce un único cambio con la lista anterior y la nueva completas.
Cuándo sirve y qué conviene mirar
Antes de publicar una configuración nueva, una revisión puede frenar el cambio si entre lo eliminado hay ajustes protegidos; una comprobación automática puede confirmar que solo cambiaron las rutas aprobadas, y una auditoría puede guardar la diferencia compacta en lugar de dos documentos grandes. Mire siempre la categoría y no solo la ruta: quitar un valor y añadir otro en otra parte no significa lo mismo que modificar uno existente. Los totales resumen el resultado, y las dos versiones deben ser registros con nombres en el primer nivel, no una lista ni un valor suelto.
Cómo leer la lista de diferencias
El resultado divide las diferencias en colecciones de elementos añadidos, eliminados y cambiados. Una entrada añadida solo existe en el objeto nuevo; una eliminada solo aparece en el original; y una cambiada incluye los valores anterior y posterior. Por ejemplo, /profile/name señala la propiedad name dentro de profile.
Este formato escapa una barra de una clave como ~1 y una virgulilla como ~0, por lo que no confunde claves literales con niveles anidados. El recorrido ordena las propiedades y produce resultados estables. El bloque counts resume cada categoría y el total.
Si las tres listas están vacías, los objetos son estructuralmente iguales aunque el texto original tuviera otra sangría u otro orden de claves.
Tratamiento de objetos anidados y arrays
Cuando una misma clave contiene un objeto en ambos lados, la comparación desciende hasta la propiedad concreta que difiere. Si profile existe en las dos entradas y solo cambia profile.active, la ruta será /profile/active. Si una clave completa solo existe en un lado, se registra una sola vez con todo su valor; no se genera una entrada por cada descendiente.
Los primitivos y null se comparan directamente. Si cambia el tipo de una propiedad, también se informa como cambio.
Qué puede hacer con ella
Revisar publicaciones de configuración
Compare la configuración desplegada con la candidata y muestre solo cambios estructurales, sin ruido de espacios ni de orden.
Proteger ajustes autorizados
Compruebe las rutas en CI y detenga la publicación si se elimina o altera una opción protegida.
Preguntas frecuentes
¿Qué formato de ruta utiliza el resultado?
Las barras de las claves se codifican como ~1 y las virgulillas como ~0.
¿El orden de las claves cuenta como cambio?
No. Los objetos se comparan por claves y valores; un orden de serialización diferente no genera diferencias.
¿Cómo se comparan los arrays?
Son valores ordenados y se comparan completos. Cualquier edición, inserción, eliminación o reordenación marca el array como cambiado.
¿Un cambio dentro de una sección aparece por separado?
Sí. Cuando las dos versiones tienen la misma sección, la comparación entra en ella y señala el dato más profundo que difiere. En el ejemplo solo cambió active dentro de profile, así que el resultado indica /profile/active y no el perfil completo.
¿Qué pasa si una sección entera es nueva?
Se anota una sola vez, con todo su contenido, en lugar de una entrada por cada dato que contiene. Así una sección de configuración recién añadida se lee como un único cambio y no como una lista larga de añadidos sueltos.
Herramientas relacionadas
Para desarrolladores — acceso por API
Todo lo de esta página está disponible por programación. Esta sección es para equipos que quieren integrarlo en sus sistemas; el resto puede usar la herramienta de arriba sin más.
Endpoint de API
¿Prefiere automatizarlo? Un POST autenticado crea la tarea; el resultado llega por webhook o enlace firmado. La misma capacidad también se ejecuta aquí en la web, por email y desde Telegram — y pronto también desde nuestra app.
Llámela desde su stack
curl -X POST https://api.kit.forhosting.com/data/diff-json-objects \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"before":{"name":"Ada","profile":{"active":true,"score":8},"roles":["reader"]},"after":{"name":"Ada","profile":{"active":false,"score":8,"city":"London"},"roles":["reader","editor"]}}'const res = await fetch("https://api.kit.forhosting.com/data/diff-json-objects", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"before": {
"name": "Ada",
"profile": {
"active": true,
"score": 8
},
"roles": [
"reader"
]
},
"after": {
"name": "Ada",
"profile": {
"active": false,
"score": 8,
"city": "London"
},
"roles": [
"reader",
"editor"
]
}
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/data/diff-json-objects",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"before": {
"name": "Ada",
"profile": {
"active": true,
"score": 8
},
"roles": [
"reader"
]
},
"after": {
"name": "Ada",
"profile": {
"active": false,
"score": 8,
"city": "London"
},
"roles": [
"reader",
"editor"
]
}
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/data/diff-json-objects", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"before":{"name":"Ada","profile":{"active":true,"score":8},"roles":["reader"]},"after":{"name":"Ada","profile":{"active":false,"score":8,"city":"London"},"roles":["reader","editor"]}}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"before":{"name":"Ada","profile":{"active":true,"score":8},"roles":["reader"]},"after":{"name":"Ada","profile":{"active":false,"score":8,"city":"London"},"roles":["reader","editor"]}}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/data/diff-json-objects", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Ejemplo de solicitud
{
"before": {
"name": "Ada",
"profile": {
"active": true,
"score": 8
},
"roles": [
"reader"
]
},
"after": {
"name": "Ada",
"profile": {
"active": false,
"score": 8,
"city": "London"
},
"roles": [
"reader",
"editor"
]
}
}Ejemplo de respuesta
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "data.diff_json_objects",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}La API es asíncrona: la llamada devuelve un task_id al instante y el resultado llega por webhook. El polling está limitado a 1 req/s por tarea.
Precio
Precio publicado — sin tokens ni créditos inventados. Una tarea fallida no se cobra.
Límites
max_mb | 25 |
Errores
| HTTP | Código | Significado |
|---|---|---|
401 | unauthorized | API key ausente o inválida. |
402 | insufficient_balance | El saldo no cubre el precio de la tarea. |
404 | unknown_type | El tipo de tarea no existe. |
429 | rate_limited | Demasiadas peticiones. Use el webhook en vez de sondear. |
Notas técnicas
Una comparación de texto puede convertir una pequeña actualización de JSON en mucho ruido: la sangría, el orden de las claves y el formato ocultan los cambios reales. Separa las claves añadidas, eliminadas y cambiadas, recorre objetos anidados compartidos y asigna a cada resultado una ruta JSON Pointer inequívoca.
Cómo leer una diferencia estructural de JSON
Cada elemento utiliza una ruta JSON Pointer que empieza por una barra.
Tratamiento de objetos anidados y arrays
Los arrays se consideran valores JSON ordenados y completos: insertar, borrar, reordenar o modificar un elemento marca la propiedad del array como cambiada. Únicamente las entradas superiores tienen una regla más estricta: before y after deben ser objetos JSON no nulos.
Uso de la comparación en revisiones y automatización
Puede bloquear una publicación si desaparece una configuración protegida, comprobar que solo cambien rutas autorizadas o guardar una diferencia compacta para auditoría. Para arrays recibirá los valores completos y podrá aplicar después reglas específicas del dominio. Una entrada superior inválida devuelve un error claro, no una diferencia vacía engañosa. Envíe el objeto original en before y el propuesto en after. La herramienta del navegador funciona localmente, mientras que una solicitud de API cuesta $0.002 y devuelve la misma estructura determinista. Como el formato y el orden de claves no producen falsos positivos, resulta útil para configuraciones, webhooks, indicadores de funciones, permisos y respuestas normalizadas. Procese las rutas como JSON Pointer y considere también la categoría del cambio.
Casos de integración
Auditar la evolución de webhooks
Registre qué campos anidados aparecen, desaparecen o cambian entre dos ejemplos normalizados de un webhook.
Preguntas técnicas
¿Puedo comparar dos arrays de nivel superior?
No. before y after deben ser objetos JSON no nulos; un array o valor primitivo superior causa un error de entrada.
¿Cuánto cuesta una solicitud de API?
La herramienta web se ejecuta localmente sin coste. Cada solicitud de API usa el precio base publicado de $0.002.