saxutils en Python: utilidades para XML

Publicado el: 23/08/2026
Tempo de leitura: 5 minutos
High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.

El módulo xml.sax.saxutils reúne funciones y clases auxiliares para aplicaciones XML basadas en SAX. Permite escapar texto, preparar valores de atributos, deshacer entidades conocidas, generar XML a partir de eventos, crear filtros entre el parser y la aplicación y normalizar fuentes de entrada.

Aunque su nombre está ligado a SAX, varias funciones son útiles fuera de un parser orientado a eventos. Aun así, deben aplicarse en el contexto correcto. Escapar XML no equivale a sanitizar HTML, parametrizar SQL, proteger comandos de shell ni validar rutas.

Herramientas principales

Las funciones más conocidas son escape(), unescape() y quoteattr(). Las clases principales son XMLGenerator y XMLFilterBase. El módulo también ofrece prepare_input_source() para normalizar entradas aceptadas por parsers.

Escapar texto con escape()

escape() sustituye &, < y > por sus entidades XML. Esto permite insertar texto dentro de un elemento sin alterar la estructura.

from xml.sax.saxutils import escape

texto = "5 < 10 & 12 > 8"
seguro = escape(texto)
print(seguro)
# 5 &lt; 10 &amp; 12 &gt; 8

Estos tres caracteres siempre se escapan. El mapping opcional entities añade sustituciones, pero no debería usarse como traductor genérico de strings.

resultado = escape(
    "línea 1\nlínea 2",
    {"\n": "
"},
)

Añade entidades personalizadas solo cuando el formato de destino lo requiera. Reemplazos arbitrarios pueden producir XML poco legible o incompatible.

El escaping depende del contexto

El texto de un elemento y el valor de un atributo son contextos diferentes. escape() no rodea el atributo con comillas ni decide cómo tratar comillas simples y dobles. Para atributos usa quoteattr().

No uses escape() como política para HTML completo enviado por usuarios. Protege una inserción en un nodo textual, pero no analiza tags, URLs, scripts, CSS ni atributos peligrosos. El artículo de html.entities en Python explica la diferencia entre entidades, decoding y sanitización.

Preparar atributos con quoteattr()

quoteattr() escapa los caracteres necesarios, elige un delimitador y devuelve el valor ya rodeado por comillas.

from xml.sax.saxutils import quoteattr

valor = 'Informe "final" & aprobado'
atributo = quoteattr(valor)
print(f"<archivo nombre={atributo}/>")

Si el valor contiene solo un tipo de comillas, la función intenta usar el otro como delimitador. Cuando contiene ambos, codifica las comillas necesarias.

Evita montar documentos XML extensos mediante concatenación. Para salida estructurada usa ElementTree, minidom o XMLGenerator. Consulta ElementTree en Python para una API de árboles.

Deshacer entidades con unescape()

unescape() convierte &amp;, &lt; y &gt; a caracteres literales.

from xml.sax.saxutils import unescape

texto = "Tom &amp; Ana &lt;3 XML"
print(unescape(texto))

Un mapping opcional puede definir entidades adicionales. Sin embargo, unescape() no es un parser XML completo. No valida estructura, namespaces, encodings, DTD ni documentos malformados.

Evita decodificar dos veces el mismo valor. Un doble decoding puede convertir texto que debía permanecer literal en markup activo. Define una única capa responsable de esa transformación.

Generar XML con XMLGenerator

XMLGenerator implementa la interfaz SAX ContentHandler y escribe eventos de vuelta como XML. Puede reproducir eventos parseados o generar un documento nuevo.

from io import StringIO
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl

salida = StringIO()
generador = XMLGenerator(
    salida,
    encoding="utf-8",
    short_empty_elements=True,
)

generador.startDocument()
generador.startElement("catalogo", AttributesImpl({"version": "1"}))
generador.startElement("producto", AttributesImpl({"id": "42"}))
generador.characters("Café & código")
generador.endElement("producto")
generador.endElement("catalogo")
generador.endDocument()

print(salida.getvalue())

El generador escapa texto y atributos según los eventos recibidos. Mantén la secuencia correctamente anidada: inicia el documento, abre elementos, escribe caracteres, cierra en orden inverso y finaliza.

Encoding de salida

El encoding configurado en XMLGenerator debe coincidir con la estrategia del stream. Con StringIO el resultado es texto Unicode. Para archivos o streams binarios, verifica cómo se producen los bytes y prueba caracteres no ASCII.

El artículo de codecs en Python explica encodings, BOM, decoding incremental y políticas de error.

Transformar eventos con XMLFilterBase

XMLFilterBase se coloca entre un XMLReader y el handler final. Por defecto transmite solicitudes y eventos sin cambios. Una subclase puede renombrar elementos, quitar atributos, normalizar texto o bloquear datos.

from xml.sax.saxutils import XMLFilterBase
from xml.sax.xmlreader import AttributesImpl

class QuitarSecreto(XMLFilterBase):
    def startElement(self, name, attrs):
        if "secreto" in attrs:
            nuevos = dict(attrs.items())
            nuevos.pop("secreto", None)
            attrs = AttributesImpl(nuevos)
        super().startElement(name, attrs)

Un filtro debe preservar una secuencia válida. Si elimina un inicio, también debe gestionar el cierre correspondiente. Prueba namespaces, texto fragmentado, comentarios, elementos vacíos e instrucciones de procesamiento.

El guía de xml.sax en Python explica handlers, namespaces, locators y callbacks.

Normalizar fuentes con prepare_input_source()

prepare_input_source() transforma un string, objeto similar a archivo o InputSource existente en una fuente lista para el parser. Una URL base puede ayudar a resolver identificadores relativos.

from xml.sax.saxutils import prepare_input_source

fuente = prepare_input_source("datos.xml")
print(fuente.getSystemId())

La resolución automática de rutas o URLs puede ser peligrosa cuando la fuente está controlada por usuarios. Restringe protocolos, hosts, directorios, redirects y tamaño. No conviertas un identificador arbitrario en una solicitud de red sin límites.

Seguridad para XML externo

El módulo no elimina vulnerabilidades del parser. Mantén desactivadas las entidades externas, limita bytes y profundidad y evita que documentos externos seleccionen archivos locales o recursos remotos. El artículo de pulldom en Python muestra límites prácticos para streams grandes.

Al generar XML, valida nombres de elementos y atributos. escape() protege contenido textual, pero no convierte un string arbitrario en un nombre XML válido. Prefiere nombres definidos por código confiable o por un esquema.

Evitar XML construido por concatenación

Un error común es unir strings:

# Evita esto en documentos reales
xml = "<usuario nombre=\"" + nombre + "\">" + texto + "</usuario>"

Incluso con escaping es fácil olvidar un contexto, producir una declaración de encoding incorrecta o desequilibrar tags. Para fragmentos mínimos, quoteattr() y escape() pueden bastar. Para documentos completos, usa una API estructural.

Pruebas recomendadas

Prueba ampersands, signos menor y mayor, ambos tipos de comillas, Unicode, valores vacíos, saltos de línea, texto ya escapado y caracteres prohibidos por XML. En generadores, prueba elementos vacíos, namespaces, atributos y streams de texto o bytes.

En filtros, parsea la salida generada durante los tests en lugar de comparar solo strings. Un documento visualmente razonable puede seguir siendo malformado.

Errores comunes

Los fallos más frecuentes son usar escape() para atributos, llamar unescape() dos veces, tratar escaping como sanitización, concatenar tags con datos externos, dejar el encoding implícito, habilitar entidades externas y modificar eventos sin preservar aperturas y cierres.

Conclusión

xml.sax.saxutils proporciona bloques prácticos para escapar texto XML, citar atributos, generar documentos por eventos, filtrar streams SAX y normalizar fuentes.

Aplica cada herramienta en su contexto, usa APIs estructurales para documentos completos y limita las entradas externas. Consulta la documentación oficial de saxutils y la especificación XML del W3C.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026