"xml.dom.minidom" --- Minimal DOM implementation
************************************************

**Código fuente:** Lib/xml/dom/minidom.py

======================================================================

"xml.dom.minidom" es una implementación mínima de la interfaz Document
Object Model (Modelo de objetos del documento), con una API similar a
la de otros lenguajes. Está destinada a ser más simple que una
implementación completa del DOM y también significativamente más
pequeña. Aquellos usuarios que aún no dominen el DOM deberían
considerar usar el módulo "xml.etree.ElementTree" en su lugar para su
procesamiento XML.

Advertencia:

  El módulo "xml.dom.minidom" no es seguro contra datos construidos
  maliciosamente. Si necesitas analizar datos que no son de confianza
  o no autenticados, consulta Vulnerabilidades XML.

Las aplicaciones DOM suelen comenzar analizando algún XML en un DOM.
Con "xml.dom.minidom", esto se hace a través de las funciones de
análisis sintáctico:

   from xml.dom.minidom import parse, parseString

   dom1 = parse('c:\\temp\\mydata.xml')  # parse an XML file by name

   datasource = open('c:\\temp\\mydata.xml')
   dom2 = parse(datasource)  # parse an open file

   dom3 = parseString('<myxml>Some data<empty/> some more data</myxml>')

La función "parse()" puede tomar un nombre de archivo o un objeto de
archivo previamente abierto.

xml.dom.minidom.parse(filename_or_file, parser=None, bufsize=None)

   Retorna un "Document" a partir de la entrada dada.
   *filename_or_file* puede ser un nombre de archivo o un objeto
   similar a un archivo. *parser*, si se proporciona, debe ser un
   objeto de un analizador sintáctico SAX2. Esta función intercambiará
   el controlador de documentos del analizador sintáctico y activará
   el soporte con el espacio de nombres. Otras configuraciones del
   analizador sintáctico (como configurar un solucionador de
   entidades) deben haberse realizado de antemano.

Si tienes XML en una cadena de caracteres, puedes usar la función
"parseString()" en su lugar:

xml.dom.minidom.parseString(string, parser=None)

   Retorna un objeto "Document" que representa a *string*. Este método
   crea un objeto "io.StringIO" para la cadena de caracteres y lo pasa
   a "parse()".

Ambas funciones retornan un objeto "Document" que representa el
contenido del documento.

Lo que hacen las funciones "parse()" y "parseString()" es conectar un
analizador sintáctico de XML con un "constructor DOM" que puede
aceptar eventos de análisis de cualquier analizador sintáctico SAX y
convertirlos en un árbol DOM. El nombre de las funciones es quizás
engañoso, pero es fácil de entender cuando se comprenden las
interfaces. El análisis sintáctico del documento se completará antes
de que retornen estas funciones, dichas funciones simplemente no
proporcionan una implementación del analizador sintáctico por si
mismas.

También puedes crear un objeto "Document" invocando a un método en un
objeto de la "Implementación del DOM". Puedes obtener este objeto
llamando a la función "getDOMImplementation()" del paquete "xml.dom" o
del módulo "xml.dom.minidom". Una vez que tengas un objeto "Document",
puedes agregarle nodos secundarios para llenar el DOM:

   from xml.dom.minidom import getDOMImplementation

   impl = getDOMImplementation()

   newdoc = impl.createDocument(None, "some_tag", None)
   top_element = newdoc.documentElement
   text = newdoc.createTextNode('Some textual content.')
   top_element.appendChild(text)

Una vez que tengas un objeto del documento DOM, puedes acceder a las
partes de tu documento XML a través de sus propiedades y métodos.
Estas propiedades se definen en la especificación DOM. La propiedad
principal del objeto del documento es "documentElement". Te
proporciona el elemento principal en el documento XML: el que contiene
a todos los demás. Aquí hay un programa de ejemplo:

   dom3 = parseString("<myxml>Some data</myxml>")
   assert dom3.documentElement.tagName == "myxml"

Cuando hayas terminado con un árbol DOM, puedes invocar opcionalmente
el método "unlink()" para forzar la limpieza temprana de los objetos
ahora innecesarios. "unlink()" es una extensión de la API del DOM
específica del módulo "xml.dom.minidom", que hace que el nodo y sus
descendientes sean esencialmente inútiles. En caso de no hacer uso de
esto, eventualmente el recolector de basura de Python se hará cargo de
los objetos en el árbol.

Ver también:

  Document Object Model (DOM) Level 1 Specification
     La recomendación del W3C para el DOM soportada por el módulo
     "xml.dom.minidom".


Objetos del DOM
===============

La definición de la API del DOM para Python se proporciona como parte
de la documentación del módulo "xml.dom". Esta sección simplemente
enumera las diferencias entre esta API y el módulo "xml.dom.minidom".

Node.unlink()

   Rompe las referencias internas dentro del DOM para recolectarlo
   como basura en las versiones de Python sin recolector de basura
   cíclico. Incluso cuando se dispone del mismo, su uso puede hacer
   que grandes cantidades de memoria estén disponibles antes, por lo
   que es una buena práctica invocar este método en objetos DOM, tan
   pronto como ya no se necesiten. Solo necesita ser invocado en el
   objeto "Document", pero se puede llamar en los nodos hijos para
   descartar los hijos de ese nodo concreto.

   Puedes evitar invocar este método explícitamente utilizando la
   declaración "with". El siguiente código desvinculará
   automáticamente *dom* cuando se salga del bloque "with":

      with xml.dom.minidom.parse(datasource) as dom:
          ... # Work with dom.

Node.writexml(writer, indent='', addindent='', newl='', encoding=None, standalone=None)

   Escribe XML en el objeto escritor. El escritor recibe texto pero no
   bytes como entrada, debe tener un método "write()" que coincida con
   el de la interfaz del objeto de archivo. El parámetro *indent* es
   la sangría del nodo actual. El parámetro *addindent* es la sangría
   incremental que se utilizará para los subnodos del nodo actual. El
   parámetro *newl* especifica la cadena que se utilizará como
   terminación de las nuevas líneas.

   Para el nodo "Document", se puede usar el argumento por palabra
   clave adicional *encoding* para especificar el valor del campo de
   codificación del encabezado XML.

   De manera similar, al indicar explícitamente el argumento
   *standalone*, las declaraciones del documento independiente se
   agregan al prólogo del documento XML. Si el valor se establece en
   "True", se agrega "standalone="yes""; de lo contrario, se establece
   en ""no"". No declarar el argumento omitirá la declaración del
   documento.

   Distinto en la versión 3.8: El método "writexml()" ahora conserva
   el orden de los atributos especificado por el usuario.

   Distinto en la versión 3.9: Se agregó el parámetro *standalone*.

Node.toxml(encoding=None, standalone=None)

   Retorna una cadena de caracteres o una cadena de bytes que contiene
   el XML representado por el nodo DOM.

   Si se proporciona de forma explícita un valor para el argumento
   *encoding* [1], el resultado es una cadena de bytes con la
   codificación especificada. Si no se proporciona el argumento
   *encoding*, el resultado es una cadena Unicode y la declaración XML
   en la cadena resultante no especifica una codificación. Codificar
   esta cadena en una codificación que no sea UTF-8 probablemente sea
   una práctica incorrecta, ya que UTF-8 es la codificación
   predeterminada para XML.

   El argumento *standalone* se comporta exactamente como en
   "writexml()".

   Distinto en la versión 3.8: El método "toxml()" ahora conserva el
   orden de los atributos especificado por el usuario.

   Distinto en la versión 3.9: Se agregó el parámetro *standalone*.

Node.toprettyxml(indent='\t', newl='\n', encoding=None, standalone=None)

   Retorna una versión impresa elegante del documento. *indent*
   especifica la cadena de caracteres a usar como sangría y es una
   tabulación por defecto; *newl* especifica la cadena de caracteres
   emitida al final de cada línea y es "\n" por defecto.

   El argumento *encoding* se comporta como el argumento
   correspondiente del método "toxml()".

   El argumento *standalone* se comporta exactamente como en
   "writexml()".

   Distinto en la versión 3.8: El método "toprettyxml()" ahora
   conserva el orden de los atributos especificado por el usuario.

   Distinto en la versión 3.9: Se agregó el parámetro *standalone*.


Ejemplo de DOM
==============

Este programa de ejemplo es una demostración bastante realista de un
programa simple. En este caso particular, no aprovechamos mucho la
flexibilidad del DOM.

   import xml.dom.minidom

   document = """\
   <slideshow>
   <title>Demo slideshow</title>
   <slide><title>Slide title</title>
   <point>This is a demo</point>
   <point>Of a program for processing slides</point>
   </slide>

   <slide><title>Another demo slide</title>
   <point>It is important</point>
   <point>To have more than</point>
   <point>one slide</point>
   </slide>
   </slideshow>
   """

   dom = xml.dom.minidom.parseString(document)

   def getText(nodelist):
       rc = []
       for node in nodelist:
           if node.nodeType == node.TEXT_NODE:
               rc.append(node.data)
       return ''.join(rc)

   def handleSlideshow(slideshow):
       print("<html>")
       handleSlideshowTitle(slideshow.getElementsByTagName("title")[0])
       slides = slideshow.getElementsByTagName("slide")
       handleToc(slides)
       handleSlides(slides)
       print("</html>")

   def handleSlides(slides):
       for slide in slides:
           handleSlide(slide)

   def handleSlide(slide):
       handleSlideTitle(slide.getElementsByTagName("title")[0])
       handlePoints(slide.getElementsByTagName("point"))

   def handleSlideshowTitle(title):
       print(f"<title>{getText(title.childNodes)}</title>")

   def handleSlideTitle(title):
       print(f"<h2>{getText(title.childNodes)}</h2>")

   def handlePoints(points):
       print("<ul>")
       for point in points:
           handlePoint(point)
       print("</ul>")

   def handlePoint(point):
       print(f"<li>{getText(point.childNodes)}</li>")

   def handleToc(slides):
       for slide in slides:
           title = slide.getElementsByTagName("title")[0]
           print(f"<p>{getText(title.childNodes)}</p>")

   handleSlideshow(dom)


minidom y el estándar DOM
=========================

El módulo "xml.dom.minidom" es esencialmente un DOM compatible con DOM
1.0, con algunas características de DOM 2 (principalmente
características del espacio de nombres).

El uso de la interfaz DOM en Python es sencillo. Se aplican las
siguientes reglas de mapeo:

* Se accede a las interfaces a través de objetos de instancia. Las
  aplicaciones no deben instanciar las clases en sí mismas; deben usar
  las funciones de creación disponibles en el objeto "Document". Las
  interfaces derivadas admiten todas las operaciones (y atributos) de
  las interfaces base, además de cualquier operación nueva.

* Las operaciones se utilizan como métodos. Dado que el DOM usa solo
  parámetros "in", los argumentos se pasan en el orden normal (de
  izquierda a derecha). No hay argumentos opcionales. Las operaciones
  "void" retornan "None".

* Los atributos IDL se asignan a atributos de instancia. Por
  compatibilidad con el mapeo del lenguaje OMG IDL para Python,
  también se puede acceder a un atributo "foo" a través de los métodos
  de acceso "_get_foo()" y "_set_foo()". Los atributos "readonly" no
  deben modificarse; esto no se aplica en tiempo de ejecución.

* Los tipos "short int", "unsigned int", "unsigned long long" y
  "boolean" se asignan todos a objetos enteros de Python.

* El tipo "DOMString" se asigna a cadenas de caracteres de Python. El
  módulo "xml.dom.minidom" admite bytes o cadenas de caracteres, pero
  normalmente producirá cadenas de caracteres. Los valores de tipo
  "DOMString" también pueden ser "None" cuando la especificación DOM
  del W3C permite tener el valor IDL "null".

* Las declaraciones "const" se asignan a variables en su ámbito
  respectivo (por ejemplo,
  "xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE"); no deben
  modificarse.

* "DOMException" no está actualmente soportado por el módulo
  "xml.dom.minidom". En su lugar, "xml.dom.minidom" usa excepciones
  estándar de Python como "TypeError" y "AttributeError".

* Los objetos de la clase "NodeList" se implementan usando el tipo
  lista incorporado de Python. Estos objetos proporcionan la interfaz
  definida en la especificación DOM, pero en versiones anteriores de
  Python no son compatibles con la API oficial. Sin embargo, son mucho
  más "pythónicas" que la interfaz definida en las recomendaciones del
  W3C.

Las siguientes interfaces no están implementadas en el módulo
"xml.dom.minidom":

* "DOMTimeStamp"

* "EntityReference"

La mayoría de ellas reflejan información en el documento XML que
generalmente no es de utilidad para la mayoría de los usuarios de DOM.

-[ Notas al pie ]-

[1] El nombre de codificación incluido en la salida XML debe cumplir
    con los estándares apropiados. Por ejemplo, "UTF-8" es válido,
    pero "UTF8" no es válido en la declaración de un documento XML,
    aunque Python lo acepta como nombre de codificación. Para más
    detalles, consulta https://www.w3.org/TR/2006/REC-xml11-20060816
    /#NT-EncodingDecl y https://www.iana.org/assignments/character-
    sets/character-sets.xhtml.
