# `xml.dom.minidom` --- 最小化的 DOM 实现

源代码: Lib/xml/dom/minidom.py

`xml.dom.minidom` is a minimal implementation of the Document Object Model interface, with an API similar to that in other languages. It is intended to be simpler than the full DOM and also significantly smaller. Users who are not already proficient with the DOM should consider using the `xml.etree.ElementTree` module for their XML processing instead.

备注

如果你需要解析不受信任或未经身份验证的数据，请参阅 XML 安全。

DOM applications typically start by parsing some XML into a DOM. With `xml.dom.minidom`, this is done through the parse functions:

    from xml.dom.minidom import parse, parseString

    dom1 = parse('c:\\temp\\mydata.xml')  # 解析指定名称的 XML 文件

    datasource = open('c:\\temp\\mydata.xml')
    dom2 = parse(datasource)  # 解析打开的文件

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

`parse()` 函数可接受一个文件名或者打开的文件对象。

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

  根据给定的输入返回一个 `Document`。 filename_or_file 可以是一个文件名，或是一个文件型对象。 如果给定 parser 则它必须是一个 SAX2 解析器对象。 此函数将修改解析器的处理程序并激活命名空间支持；其他解析器配置（例如设置一个实体求解器）必须已经提前完成。

如果你将 XML 存放为字符串形式，则可以改用 `parseString()` 函数:

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

  返回一个代表 string 的 `Document`。 此方法会为指定字符串创建一个 `io.StringIO` 对象并将其传递给 `parse()`。

两个函数均返回一个代表文档内容的 `Document` 对象。object representing the content of the document.

`parse()` 和 `parseString()` 函数所做的是将 XML 解析器连接到一个 "DOM 构建器"，它可以从任意 SAX 解析器接收解析事件并将其转换为 DOM 树结构。 这两个函数的名称可能有些误导性，但在学习此接口时是很容易掌握的。 文档解析操作将在这两个函数返回之前完成；简单地说这两个函数本身并不提供解析器实现。

You can also create a `Document` by calling a method on a "DOM Implementation" object. You can get this object either by calling the `getDOMImplementation()` function in the `xml.dom` package or the `xml.dom.minidom` module. Once you have a `Document`, you can add child nodes to it to populate the 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)

一旦你得到了 DOM 文档对象，你就可以通过其属性和方法访问对应 XML 文档的各个部分。 这些属性定义在 DOM 规格说明当中；文档对象的主要特征属性是 `documentElement`。 它给出了 XML 文档中的主元素：即包含了所有其他元素的元素。 以下是一个示例程序:

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

When you are finished with a DOM tree, you may optionally call the `unlink()` method to encourage early cleanup of the now-unneeded objects. `unlink()` is an `xml.dom.minidom`-specific extension to the DOM API that renders the node and its descendants essentially useless. Otherwise, Python's garbage collector will eventually take care of the objects in the tree.

参见

文档对象模型 (DOM) 第 1 层级规格说明

  The W3C recommendation for the DOM supported by `xml.dom.minidom`.

## DOM 对象

The definition of the DOM API for Python is given as part of the `xml.dom` module documentation. This section lists the differences between the API and `xml.dom.minidom`.

Node.unlink()

  破坏 DOM 的内部引用以便它能在没有循环 GC 的 Python 版本上垃圾回收器回收。 即使在循环 GC 可用的时候，使用此方法也可让大量内存更快变为可用，因此当 DOM 对象不再被需要时尽早调用它们的这个方法是很好的做法。 此方法只须在 `Document` 对象上调用，但也可以在下级节点上调用以丢弃该节点的下级节点。

  你可以通过使用 `with` 语句来避免显式调用此方法。 以下代码会在 `with` 代码块退出时自动取消链接 dom:

      with xml.dom.minidom.parse(datasource) as dom:
          ... # 操作 dom。

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

  将 XML 写入到写入器对象。 写入器接受文本而非字节串作为输入，它应当具有与文件对象接口相匹配的 `write()` 方法。 indent 形参是当前节点的缩进层级。 addindent 形参是用于当前节点的下级节点的缩进量。 newl 形参指定用于一行结束的字符串。

  对于 `Document` 节点，可以使用附加的关键字参数 encoding 来指定 XML 标头的编码格式字段。

  类似地，显式指明 standalone 参数将会使单独的文档声明被添加到 XML 文档的开头部分。 如果将该值设为 `True`，则会添加 `standalone="yes"`，否则它会被设为 `"no"`。 未指明该参数将使文档声明被省略。

  在 3.8 版本发生变更: `writexml()` 方法现在会保留用户指定的属性顺序。

  在 3.9 版本发生变更: 增加了 standalone 形参。

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

  返回一个包含 XML DOM 节点所代表的 XML 的字符串或字节串。

  带有显式的 encoding [1] 参数时，结果为使用指定编码格式的字节串。 没有 encoding 参数时，结果为 Unicode 字符串，并且结果字符串中的 XML 声明将不指定编码格式。 使用 UTF-8 以外的编码格式对此字符串进行编码通常是不正确的，因为 UTF-8 是 XML 的默认编码格式。

  standalone 参数的行为与 `writexml()` 中的完全一致。

  在 3.8 版本发生变更: `toxml()` 方法现在会保留用户指定的属性顺序。

  在 3.9 版本发生变更: 增加了 standalone 形参。

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

  返回文档的美化打印版本。 indent 指定缩进字符串并默认为制表符；newl 指定标示每行结束的字符串并默认为 `\n`。

  encoding 参数的行为类似于 `toxml()` 的对应参数。

  standalone 参数的行为与 `writexml()` 中的完全一致。

  在 3.8 版本发生变更: `toprettyxml()` 方法现在会保留用户指定的属性顺序。

  在 3.9 版本发生变更: 增加了 standalone 形参。

## DOM 示例

此示例程序是个相当实际的简单程序示例。 在这个特定情况中，我们没有过多地利用 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 和 DOM 标准

The `xml.dom.minidom` module is essentially a DOM 1.0-compatible DOM with some DOM 2 features (primarily namespace features).

Python 中 DOM 接口的用法十分直观。 会应用下列映射规则:

- 接口是通过实例对象来访问的。 应用程序不应实例化这些类本身；它们应当使用 `Document` 对象提供的创建器函数。 派生的接口支持上级接口的所有操作（和属性），并添加了新的操作。

- 操作以方法的形式使用。 因由 DOM 只使用 `in` 形参，参数是以正常顺序传入的（从左至右）。 不存在可选参数。 `void` 操作返回 `None`。

- IDL 属性会映射到实例属性。 为了兼容针对 Python 的 OMG IDL 语言映射，属性 `foo` 也可通过访问器方法 `_get_foo()` 和 `_set_foo()` 来访问。 `readonly` 属性不可被修改；运行时并不强制要求这一点。

- `short int`, `unsigned int`, `unsigned long long` 和 `boolean` 类型都会映射为 Python 整数类型。

- The type `DOMString` maps to Python strings. `xml.dom.minidom` supports either bytes or strings, but will normally produce strings. Values of type `DOMString` may also be `None` where allowed to have the IDL `null` value by the DOM specification from the W3C.

- `const` 声明会映射为它们各自的作用域内的变量 (例如 `xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE`)；它们不可被修改。

- `DOMException` is currently not supported in `xml.dom.minidom`. Instead, `xml.dom.minidom` uses standard Python exceptions such as `TypeError` and `AttributeError`.

- `NodeList` 对象是使用 Python 内置列表类型来实现的。 这些对象提供了 DOM 规格说明中定义的接口，但在较早版本的 Python 中它们不支持官方 API。 相比在 W3C 建议中定义的接口，它们要更加的 "Pythonic"。

The following interfaces have no implementation in `xml.dom.minidom`:

- `DOMTimeStamp`

- `EntityReference`

这些接口所反映的 XML 文档信息对于大多数 DOM 用户来说没有什么帮助。

备注

[1]

包括在 XML 输出中的编码格式名称应当遵循适当的标准。 例如，"UTF-8" 是有效的，但 "UTF8" 在 XML 文档的声明中是无效的，即使 Python 接受其作为编码格式名称。 详情参见 https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl 和 https://www.iana.org/assignments/character-sets/character-sets.xhtml。