O módulo xml.sax.saxutils reúne funções e classes auxiliares para aplicações XML baseadas em SAX. Ele ajuda a escapar texto, preparar valores de atributos, desfazer entidades conhecidas, gerar XML a partir de eventos e criar filtros entre o parser e o código da aplicação.
Apesar do nome ligado a SAX, algumas funções são úteis mesmo fora de um parser orientado a eventos. Ainda assim, elas precisam ser usadas no contexto correto. Escapar texto XML não é o mesmo que sanitizar HTML, validar dados, impedir injeção em SQL ou proteger comandos de shell.
As principais funções
As funções mais conhecidas são escape(), unescape() e quoteattr(). As classes principais são XMLGenerator e XMLFilterBase. O módulo também oferece prepare_input_source() para normalizar entradas aceitas por parsers.
Escapar texto com escape()
escape() substitui os caracteres &, < e > pelas entidades XML correspondentes. Isso permite colocar um texto dentro do conteúdo de um elemento sem quebrar a estrutura.
from xml.sax.saxutils import escape
texto = "5 < 10 & 12 > 8"
seguro = escape(texto)
print(seguro)
# 5 < 10 & 12 > 8
A função sempre escapa esses três caracteres. O argumento opcional entities permite adicionar substituições extras, mas não deve ser usado como mecanismo genérico de tradução de strings.
from xml.sax.saxutils import escape
resultado = escape(
"linha 1\nlinha 2",
{"\n": "
"},
)
Use substituições adicionais somente quando o formato exigir. Transformações arbitrárias podem produzir XML difícil de ler ou incompatível com o consumidor.
Escaping depende do contexto
Texto dentro de um elemento e valor de atributo são contextos diferentes. escape() não adiciona aspas ao redor de um atributo e não escolhe como tratar aspas simples e duplas. Para atributos, use quoteattr().
Também não use escape() para HTML completo recebido de usuários. Ele evita que o texto seja interpretado como markup quando inserido em um nó textual, mas não analisa políticas de tags, URLs, CSS, scripts ou atributos perigosos. O artigo de html.entities no Python explica a diferença entre entidades, decoding e sanitização.
Preparar atributos com quoteattr()
quoteattr() escapa os caracteres necessários, escolhe aspas adequadas e retorna o valor já delimitado.
from xml.sax.saxutils import quoteattr
valor = 'Relatório "final" & aprovado'
atributo = quoteattr(valor)
print(f"<arquivo nome={atributo}/>")
Se o texto contém apenas um tipo de aspas, a função tenta usar o outro como delimitador. Quando contém aspas simples e duplas, ela codifica as aspas necessárias.
Evite montar documentos XML extensos apenas com concatenação de strings. Para estruturas maiores, use ElementTree, minidom ou XMLGenerator. Veja ElementTree no Python para uma API orientada a árvores.
Desfazer entidades com unescape()
unescape() converte &, < e > para os caracteres originais.
from xml.sax.saxutils import unescape
texto = "Tom & Ana <3 XML"
print(unescape(texto))
O argumento entities permite definir entidades adicionais. Entretanto, unescape() não é um parser XML completo. Ele não valida estrutura, encoding, DTD, namespaces nem documentos malformados.
Evite decodificar repetidamente o mesmo conteúdo. Um valor transformado duas vezes pode converter texto literal em markup inesperado. Defina claramente em qual camada o decoding ocorre.
Gerar XML com XMLGenerator
XMLGenerator implementa a interface ContentHandler e escreve eventos SAX de volta como XML. Ele pode reproduzir um documento recebido ou gerar um novo fluxo de saída.
from io import StringIO
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl
saida = StringIO()
gerador = XMLGenerator(
saida,
encoding="utf-8",
short_empty_elements=True,
)
gerador.startDocument()
gerador.startElement("catalogo", AttributesImpl({"versao": "1"}))
gerador.startElement("produto", AttributesImpl({"id": "42"}))
gerador.characters("Café & código")
gerador.endElement("produto")
gerador.endElement("catalogo")
gerador.endDocument()
print(saida.getvalue())
O gerador escapa texto e atributos conforme os eventos recebidos. Use a mesma ordem lógica do XML: abra o documento, abra elementos, envie texto, feche os elementos em ordem inversa e finalize.
Encoding da saída
O encoding informado ao XMLGenerator deve corresponder ao stream de saída. Quando usar um stream binário, confirme a forma como o gerador codifica os dados. Com StringIO, o resultado é texto Unicode.
Para escrever arquivos, abra com uma estratégia coerente e teste caracteres não ASCII. O guia de codecs no Python aborda encodings, BOM e tratamento de erros.
Transformar eventos com XMLFilterBase
XMLFilterBase fica entre um XMLReader e o handler final. Por padrão, repassa eventos sem alterações. Uma subclasse pode interceptar eventos para renomear elementos, remover atributos, normalizar texto ou bloquear partes do documento.
from xml.sax.saxutils import XMLFilterBase
class RemoveSegredo(XMLFilterBase):
def startElement(self, name, attrs):
if "segredo" in attrs:
novos = dict(attrs.items())
novos.pop("segredo", None)
from xml.sax.xmlreader import AttributesImpl
attrs = AttributesImpl(novos)
super().startElement(name, attrs)
Filtros devem preservar a sequência válida de eventos. Se um elemento de início for removido, o fim correspondente também precisa ser tratado. Teste documentos com namespaces, texto fragmentado, comentários e elementos vazios.
O guia de xml.sax no Python explica handlers, namespaces e o fluxo de callbacks.
Preparar fontes com prepare_input_source()
prepare_input_source() normaliza uma string, objeto semelhante a arquivo ou InputSource para uma entrada pronta para um parser. Ele também pode resolver referências relativas a partir de uma URL base.
from xml.sax.saxutils import prepare_input_source
fonte = prepare_input_source("dados.xml")
print(fonte.getSystemId())
Resolver caminhos e URLs automaticamente pode ser arriscado quando a origem é controlada por usuários. Valide protocolos, host, diretórios permitidos e tamanho. Não transforme uma referência externa em download irrestrito.
Segurança de XML externo
O módulo não elimina vulnerabilidades do parser. Mantenha entidades externas desativadas, limite bytes e profundidade, e não permita que documentos externos escolham livremente arquivos locais ou URLs. O guia de pulldom no Python apresenta limites para documentos grandes.
Ao gerar XML, valide nomes de elementos e atributos. escape() protege conteúdo textual, mas não torna um nome arbitrário válido como tag. Prefira nomes definidos pelo código ou por um esquema confiável.
Evitar XML montado por concatenação
Um erro comum é concatenar strings para criar documentos:
# Evite em documentos reais
xml = "<usuario nome=\"" + nome + "\">" + texto + "</usuario>"
Mesmo com escaping, é fácil esquecer um contexto, errar encoding ou produzir tags desequilibradas. Para estruturas pequenas, quoteattr() e escape() podem ser suficientes; para documentos completos, use uma API estrutural.
Testes recomendados
Teste &, <, >, aspas simples e duplas, Unicode, strings vazias, quebras de linha, valores já escapados e caracteres inválidos para XML. Em geradores, teste elementos vazios, namespaces, atributos em diferentes ordens e streams de texto ou bytes.
Em filtros, confirme que a saída continua bem-formada. Faça parsing da saída gerada durante os testes em vez de comparar apenas strings.
Erros comuns
Os erros mais frequentes são usar escape() em atributos, usar unescape() duas vezes, tratar escaping como sanitização, concatenar tags com dados externos, deixar encoding implícito, habilitar entidades externas e modificar eventos sem manter pares de abertura e fechamento.
Conclusão
xml.sax.saxutils fornece blocos úteis para escapar texto XML, preparar atributos, gerar documentos a partir de eventos, transformar streams SAX e normalizar fontes de entrada.
Use cada ferramenta no contexto correto, prefira APIs estruturais para documentos completos e mantenha limites de segurança para entradas externas. Consulte a documentação oficial de saxutils e a especificação XML do W3C.







