Como comparar dois arquivos XML e ver o que mudou

A forma mais rápida de comparar dois arquivos XML é colar ambos em uma ferramenta de diff lado a lado, dar o mesmo formato e ler as linhas que ela destaca. A comparação é a parte fácil. O ruído é o que confunde as pessoas: atributos reordenados, espaços entre tags e prefixos de namespace podem fazer com que dois arquivos que significam a mesma coisa pareçam não ter nada em comum.

Este guia mostra como obter um diff XML limpo e confiável. Vamos ver por que dois documentos equivalentes se distanciam no papel, quais métodos vale a pena conhecer, e um exemplo prático que você pode seguir. Se você só quer a ferramenta, nossa página de comparação XML faz tudo isso no navegador.

Por que os arquivos XML são enganosamente difíceis de comparar

O XML tem uma gramática estrita (veja a especificação XML do W3C), mas dá aos autores muita liberdade sobre como dispor o texto. Dois documentos podem descrever exatamente os mesmos dados e ainda diferir byte a byte. Um diff de texto simples não entende nada disso, então marca tudo.

Aqui está o fato-chave a guardar: em XML, a ordem dos atributos de um elemento não é significativa. O XML Information Set trata os atributos como um conjunto não ordenado. Então <user id="7" role="admin"/> e <user role="admin" id="7"/> carregam a mesma informação, mesmo que um diff de linhas os pinte de vermelho e verde. A ordem dos elementos, por outro lado, normalmente importa.

Parece uma mudança, mas geralmente não é
O que você vê no diffÉ uma mudança real?O que fazer
Atributos em ordem diferenteNão, a ordem de atributos não é significativaCanonicalize os dois lados
Indentação de 2 vs 4 espaçosNãoFormate os dois lados igual
Espaços entre elementosGeralmente nãoFormate, ou remova o espaço insignificante
<br/> vs <br></br>Não, mesmo elemento vazioCanonicalize os dois lados
Um prefixo de namespace diferente para o mesmo URINão, prefixos são rótulos arbitráriosCompare por URI do namespace, não por prefixo
Elementos filhos em ordem diferenteGeralmente sim, a ordem dos elementos importaInvestigue, provavelmente é real

Essa última linha é a que merece atenção. A ordem dos atributos é livre, mas a ordem dos elementos filhos faz parte do documento na maioria dos esquemas. Se você quer o detalhe de como um parser vê tudo isso, o MDN tem uma sólida referência sobre análise de XML com DOMParser.

Quatro formas de comparar XML e quando usar cada uma

Não existe um único método melhor. Depende de onde os arquivos estão e do que você quer descobrir. Veja como as opções comuns se comparam.

MétodoMelhor paraEsforçoEntende XML?
Inspeção visualArquivos pequenos, um ou dois elementosBaixoNão, você é o parser
Ferramenta de diff onlineVerificações rápidas, colar de qualquer lugarBaixoCom formatação, sim
Linha de comando (xmllint)Arquivos em disco, scripting, forma canônicaMédioSim, com --c14n
IDE ou git diffArquivos já em um repositórioBaixo se já comitadoBaseado em linhas por padrão

Para a maioria das pessoas, uma ferramenta de navegador ganha em velocidade: nada para instalar, e você pode colar um fragmento direto de um arquivo de configuração ou de uma resposta SOAP. O problema é o ruído de formatação, que tratamos a seguir. Se você vive no terminal, o xmllint do libxml2 é a ferramenta a conhecer.

A comparação limpa mais rápida, passo a passo

Essa é a rotina que uso quando alguém me passa dois arquivos de configuração e pergunta "o que está diferente?". Leva cerca de quinze segundos.

  1. Abra a ferramenta de comparação XML.
  2. Cole o original à esquerda, a nova versão à direita.
  3. Clique em Formatar nos dois lados para que compartilhem a mesma indentação.
  4. Procure diferenças reais. Verde é adicionado, vermelho é removido, e um valor alterado aparece como um de cada.
  5. Ignore as linhas que são apenas reordenação de atributos ou espaços.

O passo três é quase todo o truque. Quando os dois documentos usam a mesma indentação, a única coisa que resta destacar é o que realmente mudou. Nosso motor de diff é construído sobre o diff-match-patch do Google, que compara linha por linha primeiro para se manter rápido mesmo em arquivos longos.

Um exemplo prático

Suponha que você esteja revisando uma mudança em uma configuração de serviço. Aqui está o antes:

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

E aqui está o depois, como um colega entregou a você:

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

Jogue esses em um diff de linhas cru e a primeira linha parece alterada, porque id e role trocaram de lugar. Formate ambos, compare por significado, e a história real é curta:

O que realmente mudou
AntesDepoisMudança
@roleeditoradminModificado
seats35Modificado
teamplatformAdicionado
@id77Sem mudança (apenas movido)
nameAda LovelaceAda LovelaceSem mudança

Três edições reais: uma promoção de papel, uma contagem de assentos e um novo elemento de equipe. A troca de atributos era ruído. Essa promoção de editor para admin é exatamente o tipo de coisa que você quer pegar na revisão, e é fácil de perder quando está enterrada sob uma linha que o diff marcou por engano.

XML canônico: a forma correta de ignorar o ruído

Formatar os dois lados resolve a indentação, mas existe um padrão feito exatamente para esse problema. O XML canônico, definido pelo W3C em Canonical XML 1.1, reescreve um documento em uma única forma normalizada: atributos ordenados, elementos vazios expandidos, espaços nas tags normalizados, e atributos padrão tornados explícitos. Dois documentos equivalentes produzem uma saída canônica idêntica. É o equivalente XML de ordenar as chaves do JSON.

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

Agora o diff só reporta conteúdo que realmente mudou, porque os dois arquivos foram normalizados da mesma forma. Se você só quer uma indentação legível em vez da forma canônica estrita, xmllint --format file.xml faz a formatação, que é o equivalente no terminal de clicar em Formatar no navegador.

Namespaces: a parte que confunde todo mundo

Os namespaces do XML permitem que dois documentos usem o mesmo vocabulário com rótulos de prefixo diferentes. <ns1:user> vinculado a um URI e <u:user> vinculado ao mesmo URI são o mesmo elemento; o prefixo é apenas um apelido local. Um diff de texto vê ns1 contra u e marca uma mudança que não é. A solução é comparar pelo URI do namespace em vez do prefixo, que é precisamente o que a canonicalização faz. A especificação Namespaces in XML é a referência se você precisar resolver uma discussão sobre isso.

Armadilhas comuns para observar

ArmadilhaPor que pegaSolução
Codificação de caracteresUm arquivo UTF-8 e um UTF-16 podem ter o mesmo texto mas diferir byte a byteNormalize a codificação; a declaração XML a indica
Referências a entidades&amp; e um & literal podem aparecer ambos para o mesmo caractereCanonicalize, o que resolve as entidades de forma consistente
CDATA vs texto escapado<![CDATA[a<b]]> e a&lt;b são o mesmo conteúdo de textoCompare o valor analisado, não os bytes crus
Espaços significativosDentro de xml:space="preserve", os espaços importam e não devem ser removidosNão corte às cegas; respeite xml:space
Tags auto-fechadas<x/> e <x></x> são idênticasCanonicalize para que ambas sejam renderizadas igual

Diff de texto vs diff estrutural

Tudo acima é um diff de texto: rápido, visual e perfeito para uma pessoa ler uma mudança. Um diff estrutural vai além e descreve a mudança em termos da árvore XML: este atributo mudou, aquele elemento filho foi inserido neste caminho. Você quer um diff estrutural quando um programa precisa aplicar a mudança ou quando a ordem dos elementos genuinamente não importa e você quer ignorá-la. Para a revisão do dia a dia, um diff de texto de dois documentos formatados basta.

Ferramentas relacionadas

O XML raramente é o único formato com que você lida. Se você está comparando payloads de API, comparar JSON aplica a mesma ideia ao JSON. Páginas marcadas são mais fáceis de ler na página de comparar HTML, e configurações de ambiente se alinham bem na ferramenta de comparar config.

Perguntas frequentes

Comparar arquivos XML online os envia para algum servidor?
No comparetext.org o diff roda no seu navegador. Os dois arquivos XML são comparados por JavaScript na sua própria máquina, então nada é enviado a um servidor a menos que você clique explicitamente em Salvar ou Compartilhar. Isso o torna seguro para arquivos de configuração, mensagens SOAP e outros dados que você não gostaria de colar em um site que envia tudo a cada tecla pressionada.
Por que meus dois arquivos XML mostram cada linha como diferente?
Quase sempre é formatação, não mudanças reais. Um arquivo está minificado ou indentado com tabulações, o outro com dois espaços, ou os atributos estão em uma ordem diferente. Clique em Formatar nos dois lados para que usem a mesma indentação. Depois disso o diff geralmente encolhe para o punhado de valores que realmente mudaram. Para uma normalização mais estrita, canonicalize primeiro os dois arquivos com xmllint --c14n.
A ordem dos atributos importa ao comparar XML?
Não. Em XML os atributos de um elemento são um conjunto não ordenado, então <a x="1" y="2"/> e <a y="2" x="1"/> são equivalentes. Um diff de texto simples não sabe disso e marcará a reordenação como mudança. O XML canônico ordena os atributos em uma ordem fixa, então canonicalizar os dois lados antes de comparar faz o falso positivo desaparecer. A ordem dos elementos, por outro lado, geralmente é significativa.
Como comparo XML ignorando os prefixos de namespace?
Os prefixos de namespace são rótulos locais para um URI de namespace, então ns1:user e u:user vinculados ao mesmo URI são o mesmo elemento. Para comparar corretamente, normalize por URI em vez de por prefixo. A forma mais simples é canonicalizar os dois documentos com xmllint --c14n, que reescreve as vinculações de namespace de forma consistente, e então fazer diff dos resultados. Um diff de texto cru não consegue fazer isso sozinho.
Posso comparar arquivos XML grandes sem a página travar?
Sim, até certo ponto. Um diff em modo de linha se mantém rápido em arquivos com milhares de linhas porque compara linhas inteiras primeiro em vez de cada caractere. Arquivos muito grandes (vários megabytes) são melhor tratados com uma ferramenta de linha de comando como xmllint ou git diff, que faz streaming dos dados. Para qualquer coisa que você consiga rolar confortavelmente em um navegador, um diff online é a opção mais rápida.
Qual é a diferença entre um diff de texto e um diff estrutural de XML?
Um diff de texto compara os arquivos linha por linha, da mesma forma que compararia duas redações. Um diff estrutural entende a árvore XML, então sabe que um atributo reordenado não é uma mudança e pode reportar um elemento inserido pelo seu caminho. Diffs de texto são mais rápidos e suficientes para a maioria das revisões depois que os dois lados estão formatados. Diffs estruturais importam quando um programa precisa aplicar a mudança ou quando você quer ignorar a ordem dos elementos.

Pronto para experimentar? Cole seus arquivos na ferramenta de comparação XML e veja o que mudou.