xml.dom.minidom — 最小化 DOM 实现

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


xml.dom.minidom 是文档对象模型接口的最小化实现,其 API 与其他语言中的 API 类似。它旨在比完整的 DOM 更简单,而且体积也要小得多。还不熟悉 DOM 的用户应该考虑使用 xml.etree.ElementTree 模块来进行 XML 处理。

警告

xml.dom.minidom 模块不能安全地处理恶意构建的数据。如果需要解析不受信任或未经身份验证的数据,请参阅 XML 漏洞

DOM 应用程序通常首先将一些 XML 解析为 DOM。使用 xml.dom.minidom,这是通过解析函数完成的

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>')

parse() 函数可以接受文件名或打开的文件对象。

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

从给定的输入返回一个 Documentfilename_or_file 可以是文件名,也可以是类文件对象。parser 如果给出,则必须是 SAX2 解析器对象。此函数将更改解析器的文档处理程序并激活命名空间支持;其他解析器配置(如设置实体解析器)必须事先完成。

如果字符串中有 XML,则可以使用 parseString() 函数

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

返回表示 stringDocument。此方法为字符串创建一个 io.StringIO 对象,并将其传递给 parse()

这两个函数都返回一个表示文档内容的 Document 对象。

parse()parseString() 函数的作用是将 XML 解析器与一个“DOM 构建器”连接起来,该构建器可以接受来自任何 SAX 解析器的解析事件,并将它们转换为 DOM 树。这些函数的名称可能具有误导性,但在学习接口时很容易理解。文档的解析将在这些函数返回之前完成;只是这些函数本身不提供解析器实现。

您还可以通过调用“DOM 实现”对象上的方法来创建 Document。您可以通过调用 xml.dom 包或 xml.dom.minidom 模块中的 getDOMImplementation() 函数来获取此对象。一旦您有了一个 Document,您就可以向其中添加子节点以填充 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"

当您完成 DOM 树的操作后,可以选择调用 unlink() 方法来鼓励尽早清理不再需要的对象。unlink() 是 DOM API 的 xml.dom.minidom 特有扩展,它使节点及其后代基本上无用。否则,Python 的垃圾收集器最终将负责清理树中的对象。

另请参阅

文档对象模型 (DOM) 级别 1 规范

xml.dom.minidom 支持的 DOM 的 W3C 建议。

DOM 对象

Python 的 DOM API 定义在 xml.dom 模块文档中给出。本节列出了 API 和 xml.dom.minidom 之间的差异。

断开 DOM 中的内部引用,以便在没有循环 GC 的 Python 版本上对其进行垃圾收集。即使在可以使用循环 GC 的情况下,使用此方法也可以更快地释放大量内存,因此,一旦不再需要 DOM 对象,就立即调用此方法是一种良好的做法。这只需要在 Document 对象上调用,但可以在子节点上调用以丢弃该节点的子节点。

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

with xml.dom.minidom.parse(datasource) as dom:
    ... # Work with dom.
Node.writexml(writer, indent='', addindent='', newl='', encoding=None, standalone=None)

将 XML 写入 writer 对象。writer 接收文本而不是字节作为输入,它应该有一个 write() 方法,该方法与文件对象接口的 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)

返回一个字符串或字节串,其中包含由 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 标准

xml.dom.minidom 模块本质上是一个与 DOM 1.0 兼容的 DOM,具有一些 DOM 2 功能(主要是命名空间功能)。

在 Python 中使用 DOM 接口非常简单。以下映射规则适用

  • 接口是通过实例对象访问的。应用程序不应实例化类本身;它们应使用 Document 对象上可用的创建器函数。派生接口支持来自基接口的所有操作(和属性),以及任何新操作。

  • 操作用作方法。由于 DOM 仅使用 in 参数,因此参数按正常顺序(从左到右)传递。没有可选参数。void 操作返回 None

  • IDL 属性映射到实例属性。为了与 Python 的 OMG IDL 语言映射兼容,还可以通过访问器方法 _get_foo()_set_foo() 访问属性 fooreadonly 属性不得更改;这在运行时不强制执行。

  • 类型 short intunsigned intunsigned long longboolean 都映射到 Python 整数对象。

  • 类型 DOMString 映射到 Python 字符串。xml.dom.minidom 支持字节或字符串,但通常会生成字符串。在 W3C 的 DOM 规范允许使用 IDL null 值的情况下,DOMString 类型的的值也可能是 None

  • const 声明映射到其各自作用域中的变量(例如 xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE);它们不得更改。

  • xml.dom.minidom 当前不支持 DOMException。相反,xml.dom.minidom 使用标准的 Python 异常,例如 TypeErrorAttributeError

  • NodeList 对象是使用 Python 的内置列表类型实现的。这些对象提供了 DOM 规范中定义的接口,但在早期版本的 Python 中,它们不支持官方 API。但是,它们比 W3C 建议中定义的接口更具“Python 风格”。

以下接口在 xml.dom.minidom 中没有实现

  • DOMTimeStamp

  • EntityReference

其中大多数反映了 XML 文档中对大多数 DOM 用户没有普遍效用的信息。

脚注