ElementInclude no Python: use XInclude

Publicado em: 23/08/2026
Tempo de leitura: 5 minutos
A laptop screen showing a code editor with visible programming code in a dimly lit environment.

O módulo xml.etree.ElementInclude adiciona suporte limitado a XInclude para árvores criadas com xml.etree.ElementTree. XInclude permite que um documento XML indique que outro arquivo XML ou texto deve ser inserido em determinado ponto da árvore.

Esse recurso pode simplificar configurações, documentação, catálogos e arquivos grandes divididos em partes. Porém, inclusões também criam riscos: leitura de arquivos fora do diretório esperado, acesso a URLs, ciclos, explosão de conteúdo e dependências difíceis de rastrear. Por isso, loaders personalizados, caminhos permitidos e profundidade máxima são essenciais.

O que é XInclude

XInclude usa o namespace http://www.w3.org/2001/XInclude. O elemento xi:include possui um atributo href e pode usar parse="xml" ou parse="text".

<documento xmlns:xi="http://www.w3.org/2001/XInclude">
  <titulo>Relatório</titulo>
  <xi:include href="secoes/resumo.xml" parse="xml"/>
</documento>

Ao expandir a inclusão, o elemento xi:include é substituído pela raiz do XML indicado. Para texto, o conteúdo do arquivo é inserido como string.

Primeiro exemplo

from xml.etree import ElementTree, ElementInclude

arvore = ElementTree.parse("documento.xml")
raiz = arvore.getroot()

ElementInclude.include(raiz)

arvore.write(
    "resultado.xml",
    encoding="utf-8",
    xml_declaration=True,
)

O loader padrão trata o href como nome de arquivo. Isso é conveniente para documentos locais confiáveis, mas não deve ser usado sem restrições quando o XML ou os caminhos podem ser controlados por usuários.

Integração com ElementTree

ElementInclude trabalha sobre elementos ou árvores de ElementTree. Antes de usar XInclude, domine parsing, namespaces, busca e serialização. O guia de ElementTree no Python cobre essas operações.

A expansão ocorre in-place: a árvore original é modificada. Se você precisar preservar o documento antes da inclusão, faça uma cópia segura ou carregue novamente a fonte.

parse=”xml” e parse=”text”

Com parse="xml", o loader deve retornar um Element. Com parse="text", deve retornar uma string. O encoding padrão do loader de texto é UTF-8, mas pode ser informado no elemento.

<documento xmlns:xi="http://www.w3.org/2001/XInclude">
  <rodape>
    <xi:include href="ano.txt" parse="text" encoding="utf-8"/>
  </rodape>
</documento>

Texto incluído não vira automaticamente markup XML. Ele é inserido como conteúdo textual e será escapado durante a serialização.

base_url para referências relativas

O parâmetro base_url ajuda a resolver caminhos relativos com base na localização do documento principal.

from pathlib import Path
from xml.etree import ElementTree, ElementInclude

arquivo = Path("configs/principal.xml").resolve()
arvore = ElementTree.parse(arquivo)

ElementInclude.include(
    arvore.getroot(),
    base_url=arquivo.as_uri(),
    max_depth=4,
)

Não confie apenas em base_url como proteção. Ele resolve referências, mas não impede ../, URLs externas ou caminhos absolutos quando o loader aceita esses valores.

Profundidade máxima

include() usa max_depth=6 por padrão. O limite reduz o risco de recursão excessiva e explosão de inclusões. Passar None remove o limite e raramente é uma boa ideia.

Escolha um valor menor quando a estrutura do projeto for conhecida. Para configurações com apenas um ou dois níveis, max_depth=2 pode ser suficiente.

Ciclos de inclusão

Considere a.xml incluindo b.xml e b.xml incluindo a.xml. A profundidade máxima interrompe o ciclo depois de algumas expansões, mas o sistema deveria detectar a repetição e produzir um erro claro.

Um loader personalizado pode manter um conjunto de caminhos já visitados. Como o loader recebe apenas href, parse e encoding, o estado pode ficar em um closure ou objeto chamável.

Loader seguro com diretório permitido

from pathlib import Path
from xml.etree import ElementTree

RAIZ = Path("conteudos").resolve()

class LoaderLocal:
    def __init__(self):
        self.visitados = set()

    def __call__(self, href, parse, encoding=None):
        caminho = (RAIZ / href).resolve()

        if RAIZ not in caminho.parents and caminho != RAIZ:
            raise ValueError("Inclusão fora do diretório permitido")

        if caminho in self.visitados:
            raise ValueError("Ciclo de XInclude detectado")
        self.visitados.add(caminho)

        if caminho.stat().st_size > 2 * 1024 * 1024:
            raise ValueError("Arquivo incluído é grande demais")

        if parse == "xml":
            return ElementTree.parse(caminho).getroot()
        if parse == "text":
            return caminho.read_text(encoding=encoding or "utf-8")

        raise ValueError(f"Modo de parsing inválido: {parse}")

Depois, passe a instância para include():

loader = LoaderLocal()
ElementInclude.include(
    arvore.getroot(),
    loader=loader,
    max_depth=4,
)

O exemplo bloqueia path traversal, ciclos simples e arquivos grandes. Em sistemas reais, também limite a soma de bytes de todas as inclusões, quantidade de arquivos, profundidade e tempo total.

Path.resolve() ajuda a revelar links simbólicos e componentes ... Ainda assim, existe risco de condições de corrida entre validação e abertura. Para ambientes hostis, trabalhe com diretórios controlados, permissões restritas e APIs de abertura seguras do sistema operacional.

Evitar inclusões pela rede

Um loader pode buscar URLs, mas isso cria riscos de SSRF, redirects, DNS rebinding, respostas enormes e indisponibilidade. Para XML recebido de terceiros, prefira proibir HTTP e HTTPS.

Quando a rede for realmente necessária, mantenha uma allowlist de hosts, valide IPs públicos e privados, limite redirects, timeout e bytes. O artigo de urllib.request no Python mostra controles para downloads.

Arquivos temporários e cache

Se inclusões remotas forem baixadas antes do processamento, use arquivos temporários seguros e cache com integridade. Veja tempfile no Python e hashlib no Python.

Não use o valor de href diretamente como nome de arquivo. Gere nomes internos e armazene o mapeamento separadamente.

Fallback e limitações

O suporte do Python é limitado e não oferece XPointer completo. Não presuma compatibilidade com toda a especificação XInclude ou com ferramentas XML avançadas. Teste os documentos reais do seu ecossistema.

Quando a aplicação precisa de schemas, validação complexa, XPath completo ou políticas avançadas de resolução, uma biblioteca XML especializada pode ser mais adequada.

Validação depois da expansão

A inclusão pode inserir elementos inesperados, namespaces diferentes ou dados que violam regras de negócio. Valide a árvore resultante, não apenas cada arquivo isolado.

itens = arvore.getroot().findall(".//item")
if len(itens) > 10_000:
    raise ValueError("Quantidade excessiva de itens")

for item in itens:
    if not item.get("id"):
        raise ValueError("Item sem ID")

Serialização e atomicidade

Ao salvar o resultado, escreva em um arquivo temporário no mesmo diretório e substitua o destino somente após sucesso. Isso evita deixar um XML parcial se ocorrer erro durante a expansão ou escrita.

Logs e diagnóstico

Registre o documento principal, o caminho normalizado de cada inclusão, tamanho, profundidade e resultado. Não registre conteúdo sensível. Mensagens de erro devem indicar qual inclusão falhou sem expor caminhos internos desnecessários.

Testes recomendados

Teste inclusão XML e texto, encoding inválido, arquivo ausente, caminho absoluto, ../, link simbólico, ciclo direto e indireto, profundidade excedida, arquivo muito grande, modo parse inválido, namespaces e árvore resultante.

Erros comuns

Os erros mais frequentes são usar o loader padrão com XML externo, remover max_depth, permitir URLs livres, não detectar ciclos, validar o caminho antes de resolve(), confiar no href como nome local e não validar a árvore expandida.

Conclusão

xml.etree.ElementInclude permite dividir XML em arquivos menores e expandir XInclude dentro de árvores ElementTree. O recurso é simples, mas a resolução de caminhos e recursos precisa ser tratada como operação sensível.

Use loader personalizado, diretório permitido, limites de profundidade, bytes e quantidade, e valide o resultado final. Consulte a documentação oficial de XInclude no ElementTree e a recomendação XInclude do W3C.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlreader no Python: controle parsers SAX

    Aprenda xmlreader no Python para configurar parsers SAX, InputSource, parsing incremental, atributos, locators e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    saxutils no Python: utilitários para XML

    Aprenda saxutils no Python para escapar XML, preparar atributos, gerar documentos, criar filtros SAX e evitar erros de contexto.

    Ler mais

    Tempo de leitura: 6 minutos
    23/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pulldom no Python: DOM parcial para XML

    Aprenda pulldom no Python para processar XML por eventos, expandir apenas subárvores necessárias e reduzir memória com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    23/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xml.sax no Python: processe XML em eventos

    Aprenda xml.sax no Python para processar XML por eventos com baixo uso de memória, namespaces, handlers, limites e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    minidom no Python: manipule XML com DOM

    Aprenda xml.dom.minidom no Python para ler, navegar, criar e serializar XML com DOM, namespaces, memória e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026
    Vibrant green snake coiled on a tree branch amidst lush jungle foliage.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree: leia e modifique XML no Python

    Aprenda ElementTree no Python para ler, buscar, modificar e gerar XML com namespaces, parsing incremental, limites e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026