ElementTree canonicalize: normalize XML no Python

Publicado em: 22/10/2026
Tempo de leitura: 5 minutos
Estrutura de código e XML para canonicalização com Python

Assinar, comparar ou versionar documentos XML exige mais do que simplesmente salvar a mesma árvore duas vezes. Espaços, ordem de atributos, declarações de namespace e outros detalhes podem mudar a representação textual sem alterar o significado dos dados. A função xml.etree.ElementTree.canonicalize() resolve esse problema ao produzir uma forma canônica de XML, adequada para comparações estáveis, auditoria e preparação de conteúdo para assinaturas digitais.

Neste guia, você aprenderá o que é canonicalização XML, como usar ElementTree.canonicalize, quais opções controlam a saída e quais cuidados de segurança e compatibilidade devem fazer parte de uma implementação real.

O que é XML canônico

Dois documentos XML podem representar a mesma estrutura e ainda possuir textos diferentes. Atributos podem aparecer em ordens distintas, namespaces podem usar prefixos diferentes e espaços podem variar. Uma comparação simples de strings consideraria esses documentos diferentes. A canonicalização transforma o XML em uma representação previsível, seguindo regras definidas pelo padrão C14N.

Isso é útil em testes, caches, deduplicação, controle de versão e sistemas que calculam hashes. Também é uma etapa comum antes de assinar XML, porque qualquer diferença textual posterior altera o resumo criptográfico.

Exemplo básico

from xml.etree.ElementTree import canonicalize

xml = '''
<pedido xmlns:x="https://exemplo.com/item" id="42" status="pago">
  <x:produto quantidade="2" codigo="A1" />
</pedido>
'''

resultado = canonicalize(xml_data=xml)
print(resultado)

A função recebe o XML por xml_data e retorna texto Unicode. Também é possível informar um arquivo de saída por meio de out. O resultado reorganiza detalhes que não devem influenciar a equivalência estrutural.

Lendo de arquivo

from xml.etree.ElementTree import canonicalize

with open("entrada.xml", encoding="utf-8") as origem:
    with open("canonico.xml", "w", encoding="utf-8") as destino:
        canonicalize(from_file=origem, out=destino)

Quando o XML já está em disco, from_file evita carregar manualmente o conteúdo. O arquivo de destino deve ser aberto em modo texto com UTF-8. Essa escolha reduz ambiguidades e mantém a saída consistente entre ambientes.

Removendo comentários

Comentários normalmente não participam do significado dos dados. A opção with_comments define se eles permanecem no resultado.

canonico = canonicalize(
    xml_data=xml,
    with_comments=False,
)

Para hashes e comparações de configuração, remover comentários costuma ser adequado. Em auditorias ou fluxos documentais, eles podem ser relevantes e devem ser preservados.

Controlando espaços com strip_text

A opção strip_text remove espaços antes e depois do conteúdo textual. Use-a apenas quando esses espaços não tiverem significado para o domínio.

canonico = canonicalize(
    xml_data="<nome>  Ana  </nome>",
    strip_text=True,
)

Em XML de configuração, isso pode ajudar. Em documentos editoriais, mensagens ou formatos nos quais espaços são significativos, a remoção pode alterar o conteúdo. Teste sempre com amostras reais.

Reescrevendo prefixos de namespace

Prefixos diferentes podem apontar para o mesmo namespace. A opção rewrite_prefixes substitui os prefixos originais por nomes previsíveis, como n0 e n1.

canonico = canonicalize(
    xml_data=xml,
    rewrite_prefixes=True,
)

Essa opção é útil quando documentos equivalentes chegam de sistemas que escolhem prefixos diferentes. O URI do namespace continua sendo o identificador real; o prefixo é apenas uma forma abreviada.

Atributos e tags que contêm nomes qualificados

Alguns formatos guardam nomes com prefixos dentro de valores textuais. Nesses casos, o canonicalizador precisa saber quais atributos ou tags contêm QNames. Use qname_aware_tags e qname_aware_attrs.

canonico = canonicalize(
    xml_data=xml,
    rewrite_prefixes=True,
    qname_aware_attrs={"tipo"},
    qname_aware_tags={"expressao"},
)

Sem essa configuração, um prefixo reescrito na estrutura pode permanecer antigo dentro do texto, criando inconsistência semântica.

Excluindo elementos e atributos

As opções exclude_tags e exclude_attrs permitem ignorar partes voláteis, como timestamps, identificadores de rastreamento ou campos calculados.

canonico = canonicalize(
    xml_data=xml,
    exclude_attrs={"gerado_em", "request_id"},
    exclude_tags={"debug"},
)

Isso facilita testes de snapshot e deduplicação, mas exige cuidado. Excluir campos relevantes pode fazer documentos diferentes parecerem iguais. Defina a política por domínio e documente-a.

Gerando hashes estáveis

from hashlib import sha256
from xml.etree.ElementTree import canonicalize

def hash_xml(xml_texto: str) -> str:
    canonico = canonicalize(
        xml_data=xml_texto,
        with_comments=False,
        rewrite_prefixes=True,
    )
    return sha256(canonico.encode("utf-8")).hexdigest()

O hash será estável somente se todos os participantes aplicarem exatamente as mesmas opções. Guarde a versão da política de canonicalização junto ao hash ou ao protocolo.

Comparando documentos em testes

def xml_equivalente(a: str, b: str) -> bool:
    opcoes = {
        "with_comments": False,
        "rewrite_prefixes": True,
        "strip_text": True,
    }
    return canonicalize(xml_data=a, **opcoes) == canonicalize(xml_data=b, **opcoes)

Esse método é melhor do que comparar XML bruto, mas não substitui validação de esquema ou regras do negócio. Dois documentos canônicos iguais são textualmente equivalentes segundo a política escolhida; isso não garante que sejam válidos para uma API.

Canonicalização e assinatura digital

A canonicalização é somente uma parte de uma assinatura XML. Ela não calcula a assinatura, não valida certificados e não protege contra todas as transformações possíveis. Sistemas de assinatura devem seguir o algoritmo, as transformações e o perfil exigidos pelo protocolo. Para cenários críticos, use bibliotecas especializadas em XML Signature e valide a interoperabilidade com os outros participantes.

Nunca implemente um protocolo criptográfico apenas concatenando canonicalize() com uma chave privada sem compreender o padrão completo.

Segurança no parsing

XML externo pode causar consumo excessivo de recursos ou explorar comportamentos perigosos em parsers inadequados. Limite o tamanho dos documentos, valide a origem, imponha timeouts e considere bibliotecas reforçadas quando o conteúdo não for confiável. A canonicalização não sanitiza automaticamente o XML nem valida sua segurança.

Leia também os artigos sobre ElementTree no Python, hashes com hashlib, arquivos com pathlib e a categoria de Python Avançado.

Boas práticas

Defina uma única função central para canonicalizar XML, evitando opções diferentes espalhadas pelo projeto. Escreva testes com namespaces, atributos fora de ordem, comentários, espaços, caracteres Unicode e campos excluídos. Registre a política usada e não altere opções silenciosamente em sistemas que dependem de hashes antigos.

Quando a saída for persistida, use UTF-8 e modo texto. Ao comparar documentos muito grandes, estabeleça limites de memória e considere o impacto de manter strings completas em memória.

Referências externas

Consulte a documentação oficial de ElementTree.canonicalize e a recomendação XML Canonicalization do W3C.

Conclusão

ElementTree.canonicalize oferece uma forma prática de transformar XML em texto estável. Ela é útil para testes, hashes, auditoria e integração com sistemas que exigem representação canônica. O resultado depende das opções escolhidas, portanto trate a configuração como uma política formal. Com validação, limites de segurança e testes de interoperabilidade, a função reduz falsos positivos em comparações e torna pipelines XML mais previsíveis.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código de programação Python em uma tela
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    T-strings e templatelib: interpolação estruturada

    Aprenda t-strings e string.templatelib no Python para processar interpolações, criar renderizadores seguros e preservar dados estruturados.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Banco SQLite e código Python para exportação seletiva com iterdump
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crie dumps seletivos no Python

    Aprenda sqlite3.Connection.iterdump com filtro para gerar dumps seletivos, validar dependências e restaurar bancos com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    21/10/2026
    Código e arquivos representando compressão de dados no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido no Python

    Aprenda a usar compression.zstd no Python para comprimir e descomprimir dados com desempenho, streaming e segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Arquivo de configuração e estrutura de código para ConfigParser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: leia INI sem seção no Python

    Aprenda a ler arquivos INI sem cabeçalho usando ConfigParser, com validação, tipos, compatibilidade e escrita segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Dados binários e memória de computador representando memoryview no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busque sem copiar

    Aprenda memoryview count e index no Python para buscar valores em buffers sem cópias e validar dados binários com eficiência.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Desenvolvedora trabalhando com código Python e validação de dados
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    map strict: detecte iteráveis desalinhados

    Aprenda map strict no Python para validar iteráveis, evitar truncamento silencioso e detectar dados desalinhados.

    Ler mais

    Tempo de leitura: 6 minutos
    19/10/2026