Cómo comparar dos archivos XML y ver qué cambió

La forma más rápida de comparar dos archivos XML es pegar ambos en una herramienta de diff lado a lado, darles el mismo formato y leer las líneas que resalta. La comparación es la parte fácil. El ruido es lo que confunde a la gente: atributos reordenados, espacios entre etiquetas y prefijos de namespace pueden hacer que dos archivos que significan lo mismo parezcan no compartir nada en absoluto.

Esta guía explica cómo obtener un diff XML limpio y confiable. Veremos por qué dos documentos equivalentes se distancian en papel, qué métodos vale la pena conocer, y un ejemplo práctico que puedes seguir. Si solo quieres la herramienta, nuestra página de comparación XML hace todo esto en el navegador.

Por qué los archivos XML son engañosamente difíciles de comparar

XML tiene una gramática estricta (consulta la especificación XML de la W3C), pero da a los escritores mucha libertad sobre cómo disponer el texto. Dos documentos pueden describir exactamente los mismos datos y aun así diferir byte a byte. Un diff de texto plano no entiende nada de eso, así que lo marca todo.

Este es el dato clave que debes recordar: en XML, el orden de los atributos de un elemento no es significativo. El XML Information Set trata los atributos como un conjunto desordenado. Así que <user id="7" role="admin"/> y <user role="admin" id="7"/> contienen la misma información, aunque un diff de líneas los pinte de rojo y verde. El orden de los elementos, en cambio, normalmente sí importa.

Parece un cambio, pero normalmente no lo es
Lo que ves en el diff¿Es un cambio real?Qué hacer
Atributos en orden diferenteNo, el orden de atributos no es significativoCanonicaliza ambos lados
Indentación de 2 vs 4 espaciosNoFormatea ambos lados igual
Espacios entre elementosNormalmente noFormatea, o elimina el espacio insignificante
<br/> vs <br></br>No, mismo elemento vacíoCanonicaliza ambos lados
Un prefijo de namespace diferente para el mismo URINo, los prefijos son etiquetas arbitrariasCompara por URI del namespace, no por prefijo
Elementos hijos en orden diferenteNormalmente sí, el orden de elementos importaInvestígalo, probablemente sea real

Esa última fila es la que hay que vigilar. El orden de atributos es libre, pero el orden de los elementos hijos forma parte del documento en la mayoría de los esquemas. Si quieres el detalle sobre cómo un parser ve todo esto, MDN tiene una sólida referencia sobre cómo parsear XML con DOMParser.

Cuatro formas de comparar XML y cuándo usar cada una

No existe un único método mejor. Depende de dónde estén los archivos y qué quieres descubrir. Así se comparan las opciones más comunes.

MétodoMejor paraEsfuerzo¿Entiende XML?
Revisión visualArchivos pequeños, uno o dos elementosBajoNo, tú eres el parser
Herramienta diff onlineVerificaciones rápidas, pegar desde cualquier lugarBajoCon formato, sí
Línea de comandos (xmllint)Archivos en disco, scripting, forma canónicaMedioSí, con --c14n
IDE o git diffArchivos ya en un repositorioBajo si está confirmadoBasado en líneas por defecto

Para la mayoría de las personas, una herramienta de navegador gana en velocidad: no hay nada que instalar y puedes pegar un fragmento directamente desde un archivo de configuración o una respuesta SOAP. El problema es el ruido de formato, que manejaremos a continuación. Si vives en la terminal, xmllint de libxml2 es la herramienta que debes conocer.

La comparación limpia más rápida, paso a paso

Esta es la rutina que uso cuando alguien me da dos archivos de configuración y pregunta "¿qué es diferente?" Tarda unos quince segundos.

  1. Abre la herramienta de comparación XML.
  2. Pega el original a la izquierda y la nueva versión a la derecha.
  3. Haz clic en Formato en ambos lados para que compartan la misma indentación.
  4. Busca diferencias reales. El verde es añadido, el rojo es eliminado, y un valor modificado aparece como uno de cada color.
  5. Ignora las filas que solo son reordenamiento de atributos o espacios.

El paso tres es casi todo el truco. Una vez que ambos documentos usan la misma indentación, lo único que queda por resaltar es lo que realmente cambió. Nuestro motor de diff está construido sobre el diff-match-patch de Google, que compara línea por línea primero para que sea rápido incluso en archivos largos.

Un ejemplo práctico

Supón que estás revisando un cambio en una configuración de servicio. Aquí está el antes:

<user id="7" role="editor">
  <name>Ada Lovelace</name>
  <active>true</active>
  <seats>3</seats>
</user>

Y aquí está el después, tal como te lo entregó un compañero:

<user role="admin" id="7">
  <name>Ada Lovelace</name>
  <active>true</active>
  <seats>5</seats>
  <team>platform</team>
</user>

Mete esos en un diff de líneas crudo y la primera línea parece cambiada, porque id y role intercambiaron lugares. Formatea ambos, compara por significado, y la historia real es breve:

Lo que realmente cambió
NodoAntesDespuésCambio
@roleeditoradminModificado
seats35Modificado
teamplatformAñadido
@id77Sin cambios (solo se movió)
nameAda LovelaceAda LovelaceSin cambios

Tres ediciones reales: un ascenso de rol, un conteo de asientos y un nuevo elemento de equipo. El intercambio de atributos era ruido. Ese ascenso de editor a admin es exactamente el tipo de cosa que quieres detectar en revisión, y es fácil perdérselo cuando está enterrado bajo una línea que el diff marcó por error.

XML canónico: la forma correcta de ignorar el ruido

Formatear ambos lados maneja la indentación, pero existe un estándar diseñado exactamente para este problema. El XML canónico, definido por la W3C en Canonical XML 1.1, reescribe un documento en una única forma normalizada: atributos ordenados, elementos vacíos expandidos, espacios en las etiquetas normalizados, y atributos por defecto hechos explícitos. Dos documentos equivalentes producen una salida canónica idéntica. Es el equivalente XML de ordenar las claves de JSON.

xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml

Ahora diff solo reporta contenido que realmente cambió, porque ambos archivos han sido normalizados de la misma forma. Si solo quieres una indentación legible en lugar de la forma canónica estricta, xmllint --format file.xml lo formatea, que es el equivalente en terminal de hacer clic en Formato en el navegador.

Namespaces: la parte que confunde a todos

Los namespaces de XML permiten que dos documentos usen el mismo vocabulario con etiquetas de prefijo diferentes. <ns1:user> vinculado a un URI y <u:user> vinculado al mismo URI son el mismo elemento; el prefijo es solo un apodo local. Un diff de texto ve ns1 contra u y marca un cambio que no lo es. La solución es comparar por el URI del namespace en lugar del prefijo, que es precisamente lo que hace la canonicalización. La especificación Namespaces in XML es la referencia si necesitas zanjar una discusión al respecto.

Errores comunes que debes vigilar

ErrorPor qué dueleSolución
Codificación de caracteresUn archivo UTF-8 y uno UTF-16 pueden contener el mismo texto pero diferir byte a byteNormaliza la codificación; la declaración XML la indica
Referencias a entidades&amp; y un & literal pueden aparecer ambos para el mismo carácterCanonicaliza, lo que resuelve las entidades de forma consistente
CDATA vs texto escapado<![CDATA[a<b]]> y a&lt;b son el mismo contenido de textoCompara el valor parseado, no los bytes crudos
Espacios significativosDentro de xml:space="preserve", los espacios importan y no deben eliminarseNo recortes a ciegas; respeta xml:space
Etiquetas autocerradas<x/> y <x></x> son idénticasCanonicaliza para que ambas se rendericen igual

Diff de texto vs diff estructural

Todo lo anterior es un diff de texto: rápido, visual y perfecto para que una persona lea un cambio. Un diff estructural va más lejos y describe el cambio en términos del árbol XML: este atributo cambió, ese elemento hijo se insertó en esta ruta. Quieres un diff estructural cuando un programa necesita aplicar el cambio o cuando el orden de los elementos genuinamente no importa y quieres ignorarlo. Para la revisión diaria, un diff de texto de dos documentos formateados es más que suficiente.

Herramientas relacionadas

XML rara vez es el único formato con el que trabajas. Si estás comparando payloads de API, comparar JSON aplica la misma idea a JSON. Las páginas con marcado se leen más fácilmente en la página de comparar HTML, y la configuración de entornos se alinea bien en la herramienta de comparar config.

Preguntas frecuentes

¿Comparar archivos XML online los sube a algún servidor?
En comparetext.org el diff se ejecuta en tu navegador. Los dos archivos XML son comparados por JavaScript en tu propia máquina, así que nada se envía a un servidor a menos que hagas clic explícitamente en Guardar o Compartir. Esto lo hace seguro para archivos de configuración, mensajes SOAP y otros datos que no querrías pegar en un sitio que sube todo en cada pulsación de tecla.
¿Por qué mis dos archivos XML muestran cada línea como diferente?
Casi siempre es el formato, no cambios reales. Un archivo está minificado o indentado con tabulaciones, el otro con dos espacios, o los atributos están en un orden diferente. Haz clic en Formato en ambos lados para que usen la misma indentación. Después de eso el diff generalmente se reduce al puñado de valores que realmente cambiaron. Para una normalización más estricta, canonicaliza primero ambos archivos con xmllint --c14n.
¿Importa el orden de los atributos al comparar XML?
No. En XML los atributos de un elemento son un conjunto desordenado, así que <a x="1" y="2"/> y <a y="2" x="1"/> son equivalentes. Un diff de texto plano no lo sabe y marcará el reordenamiento como un cambio. El XML canónico ordena los atributos en un orden fijo, así que canonicalizar ambos lados antes de comparar hace que el falso positivo desaparezca. El orden de los elementos, en cambio, normalmente sí es significativo.
¿Cómo comparo XML ignorando los prefijos de namespace?
Los prefijos de namespace son etiquetas locales para un URI de namespace, así que ns1:user y u:user vinculados al mismo URI son el mismo elemento. Para comparar correctamente, normaliza por URI en lugar de por prefijo. La forma más simple es canonicalizar ambos documentos con xmllint --c14n, que reescribe las vinculaciones de namespace de forma consistente, y luego hacer diff de los resultados. Un diff de texto crudo no puede hacer esto por sí solo.
¿Puedo comparar archivos XML grandes sin que la página se congele?
Sí, hasta cierto punto. Un diff en modo de líneas se mantiene rápido en archivos con miles de líneas porque compara líneas completas primero en lugar de cada carácter. Los archivos muy grandes (varios megabytes) se manejan mejor con una herramienta de línea de comandos como xmllint o git diff, que transmite los datos. Para cualquier cosa que puedas desplazarte cómodamente en un navegador, un diff online es la opción más rápida.
¿Cuál es la diferencia entre un diff de texto y un diff estructural de XML?
Un diff de texto compara los archivos línea por línea, de la misma forma que compararía dos ensayos. Un diff estructural entiende el árbol XML, así que sabe que un atributo reordenado no es un cambio y puede reportar un elemento insertado por su ruta. Los diffs de texto son más rápidos y suficientes para la mayoría de las revisiones una vez que ambos lados están formateados. Los diffs estructurales importan cuando un programa necesita aplicar el cambio o cuando quieres ignorar el orden de los elementos.

¿Listo para probarlo? Pega tus archivos en la herramienta de comparación XML y ve qué cambió.