xml.dom.minidom — 最小 DOM 实现

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


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

备注

如果您需要解析不受信任或未经身份验证的数据,请参阅 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 包中的 getDOMImplementation() 函数或 xml.dom.minidom 模块来获取此对象。一旦您拥有 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()xml.dom.minidom 特定于 DOM API 的扩展,它使节点及其后代基本无用。否则,Python 的垃圾回收器最终会处理树中的对象。

参见

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

xml.dom.minidom 支持的 W3C DOM 推荐标准。

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() 方法。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 属性映射到实例属性。为了与 OMG IDL Python 语言映射兼容,属性 foo 也可以通过访问器方法 _get_foo()_set_foo() 访问。readonly 属性不得更改;这在运行时不强制执行。

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

  • 类型 DOMString 映射到 Python 字符串。xml.dom.minidom 支持字节或字符串,但通常会生成字符串。类型为 DOMString 的值也可以是 None,如果 W3C 的 DOM 规范允许其具有 IDL null 值。

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

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

  • NodeList 对象使用 Python 的内置列表类型实现。这些对象提供 DOM 规范中定义的接口,但在早期版本的 Python 中,它们不支持官方 API。然而,它们比 W3C 推荐标准中定义的接口更“Pythonic”。

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

  • DOMTimeStamp

  • EntityReference

其中大部分反映了 XML 文档中对大多数 DOM 用户没有普遍实用价值的信息。

脚注