xml.dom.minidom es una implementación compacta del Document Object Model en la biblioteca estándar de Python. En lugar de centrarse solo en elementos e hijos, DOM representa documentos, elementos, atributos, nodos de texto, comentarios y otros tipos mediante una API similar a la de navegadores y otros lenguajes.
El módulo resulta útil cuando una integración exige conceptos DOM, cuando necesitas manipular nodos individualmente o al portar código de otra plataforma. Para la mayoría de trabajos comunes con XML, la documentación recomienda ElementTree en Python, normalmente más sencillo y eficiente en memoria.
Parsear un archivo
from xml.dom import minidom
with minidom.parse("catalogo.xml") as documento:
raiz = documento.documentElement
print(raiz.tagName)
parse() acepta un nombre de archivo u objeto similar a archivo y devuelve un Document. El context manager llama unlink() al salir, permitiendo liberar referencias internas antes.
El parsing construye todo el árbol DOM antes de retornar. No uses minidom con documentos ilimitados o muy grandes sin controles estrictos.
Parsear una cadena
xml = "<producto id='7'><nombre>Teclado</nombre></producto>"
with minidom.parseString(xml) as documento:
producto = documento.documentElement
print(producto.getAttribute("id"))
Antes de parseString(), impone un límite de bytes o caracteres. Una fuente XML relativamente pequeña puede producir muchos objetos según su estructura.
Tipos de nodos
Cada nodo expone nodeType. Las constantes comunes incluyen DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE, COMMENT_NODE y PROCESSING_INSTRUCTION_NODE.
from xml.dom import Node
for nodo in producto.childNodes:
if nodo.nodeType == Node.ELEMENT_NODE:
print("elemento", nodo.tagName)
elif nodo.nodeType == Node.TEXT_NODE:
print("texto", repr(nodo.data))
Los espacios y saltos entre etiquetas también pueden convertirse en nodos de texto. Nunca supongas que el primer hijo es un elemento.
Buscar elementos
productos = documento.getElementsByTagName("producto")
for producto in productos:
print(producto.getAttribute("id"))
getElementsByTagName() busca recursivamente entre descendientes. Para hijos directos, filtra childNodes. Las búsquedas recursivas repetidas pueden costar tiempo; crea índices de aplicación cuando consultes mucho un documento.
Extraer texto correctamente
Un elemento puede contener varios nodos de texto mezclados con elementos. Define si deseas texto directo o contenido recursivo.
from xml.dom import Node
def texto_directo(elemento: Node) -> str:
partes = []
for hijo in elemento.childNodes:
if hijo.nodeType == Node.TEXT_NODE:
partes.append(hijo.data)
return "".join(partes)
Para texto recursivo, recorre descendientes. Decide cómo tratar comentarios, CDATA y espacios en lugar de aplicar una regla implícita.
Atributos
producto.setAttribute("activo", "si")
identificador = producto.getAttribute("id")
if producto.hasAttribute("temporal"):
producto.removeAttribute("temporal")
getAttribute() devuelve cadena vacía si el atributo no existe. Usa hasAttribute() cuando importe distinguir ausencia de valor vacío.
Crear un documento
from xml.dom.minidom import getDOMImplementation
implementacion = getDOMImplementation()
documento = implementacion.createDocument(None, "catalogo", None)
raiz = documento.documentElement
producto = documento.createElement("producto")
producto.setAttribute("id", "7")
raiz.appendChild(producto)
nombre = documento.createElement("nombre")
nombre.appendChild(documento.createTextNode("Teclado"))
producto.appendChild(nombre)
Crea nodos mediante su Document. No instancies clases internas de minidom directamente.
Añadir y eliminar nodos
precio = documento.createElement("precio")
precio.appendChild(documento.createTextNode("199.90"))
producto.appendChild(precio)
producto.removeChild(precio)
precio.unlink()
removeChild() separa un nodo, pero el objeto puede conservar referencias. unlink() vuelve inutilizable el nodo y sus descendientes y favorece liberar memoria.
Clonar nodos
copia = producto.cloneNode(deep=True)
copia.setAttribute("id", "8")
raiz.appendChild(copia)
Con deep=False, solo se copia el nodo. Revisa identificadores y referencias antes de insertar clones para evitar IDs duplicados.
Namespaces
URI = "https://example.com/catalogo"
documento = implementacion.createDocument(URI, "cat:catalogo", None)
producto = documento.createElementNS(URI, "cat:producto")
documento.documentElement.appendChild(producto)
Usa métodos con sufijo NS, como createElementNS(), getElementsByTagNameNS() y setAttributeNS(). El prefijo es una elección de serialización; la URI identifica el namespace.
Serialización con toxml
datos = documento.toxml(
encoding="utf-8",
standalone=True,
)
with open("catalogo.xml", "wb") as archivo:
archivo.write(datos)
Con encoding explícito, toxml() devuelve bytes. Sin él, devuelve string Unicode. Usa nombres válidos en declaraciones, como UTF-8.
Pretty print
bonito = documento.toprettyxml(
indent=" ",
newl="\n",
encoding="utf-8",
)
toprettyxml() mejora legibilidad, pero puede añadir espacios que cambian texto mixto significativo. No uses pretty print para documentos firmados o comparaciones byte a byte.
writexml
with open("catalogo.xml", "w", encoding="utf-8") as archivo:
documento.writexml(
archivo,
addindent=" ",
newl="\n",
encoding="UTF-8",
)
El writer de writexml() recibe texto, no bytes. Combina correctamente el modo del archivo y la interfaz.
Salida atómica
Escribe en un archivo temporal del mismo directorio, haz flush cuando corresponda y reemplaza la ruta final solo después del éxito. Consulta tempfile en Python.
Memoria y unlink
DOM mantiene relaciones padre-hijo y todo el árbol en memoria. Los documentos grandes pueden consumir mucho más que APIs de streaming. Llama documento.unlink() al terminar o usa with minidom.parse(...) as documento.
Después de unlink, no reutilices nodos. La operación sirve para limpieza, no para reset parcial.
XML externo y seguridad
La documentación remite a las vulnerabilidades XML de Python. La entrada no confiable puede explotar expansión de entidades, profundidad y consumo de recursos. Limita tamaño, actualiza el runtime y considera bibliotecas endurecidas.
No permitas que una URL o ruta controlada por usuario pase directamente a parse(). Separa obtención, validación de destino y parsing para reducir SSRF y lectura local.
Parser SAX configurado
parse() puede recibir un parser SAX2 configurado previamente. Esto permite instalar un entity resolver o features antes del constructor DOM.
import xml.sax
from xml.dom import minidom
parser = xml.sax.make_parser()
# Configura features y resolver antes del parsing.
documento = minidom.parse("datos.xml", parser=parser)
Minidom cambia el document handler y activa namespaces. Prueba la configuración exacta con la versión de Python de producción.
Comentarios e instrucciones
DOM puede preservar comentarios y processing instructions. Trata su contenido como no confiable. Las transformaciones deben decidir explícitamente si conservarlos o eliminarlos.
Minidom frente a ElementTree
ElementTree ofrece una API más Pythonica para la mayoría de XML e incluye procesamiento incremental. Minidom tiene sentido cuando necesitas el modelo completo DOM, tipos de nodo explícitos o compatibilidad con código basado en W3C.
Validación
Parsing no valida la estructura de negocio. Después de construir el DOM, comprueba raíz, namespaces, atributos obligatorios, cardinalidad, tipos, rangos y relaciones. Minidom no valida XSD automáticamente.
Errores
XML malformado suele lanzar una excepción Expat o SAX. Captura errores alrededor del parsing y conserva la causa sin exponer el documento completo.
from xml.parsers.expat import ExpatError
try:
documento = minidom.parseString(xml_recibido)
except ExpatError as error:
raise ValueError(
f"XML inválido en línea {error.lineno}, columna {error.offset}"
) from error
Pruebas recomendadas
Prueba nodos de texto con espacios, comentarios, CDATA, atributos ausentes y vacíos, namespaces, clonación profunda, eliminación, pretty print, encoding, documentos grandes, XML malformado, profundidad excesiva, unlink y validación de dominio.
Errores comunes
Los fallos frecuentes son usar el primer childNode sin comprobar tipo, confundir atributo ausente con vacío, retener DOM grandes, olvidar cleanup, aplicar pretty print a texto mixto, crear nodos sin Document, ignorar namespaces, parsear XML ilimitado y tratar parsing como validación.
Conclusión
xml.dom.minidom proporciona una implementación DOM compacta y familiar. Úsala cuando importe el modelo completo de nodos. Para XML directo o archivos grandes, ElementTree o SAX suelen ser mejores.
Consulta la documentación oficial de minidom y la especificación DOM Level 1. Para XML externo, combina límites estrictos, parser actualizado, validación y cleanup explícito.







