Skip to main content

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

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


xml.dom.minidom 是文档对象模型接口的最小实现,具有类似于其他语言的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级规范

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

20.7.1. DOM对象

Python 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="")

将XML写入writer对象。作者应该有一个与文件对象接口匹配的 write() 方法。 indent 参数是当前节点的缩进。 addindent 参数是用于当前子节点的增量缩进。 newl 参数指定用于终止换行符的字符串。

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

Node.toxml(encoding=None)

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

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

Node.toprettyxml(indent="", newl="", encoding="")

返回文档的漂亮打印版本。 indent 指定缩进字符串,默认为制表符; newl 指定在每行结尾处发出的字符串,默认为 \n

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

20.7.2. 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("<title>%s</title>" % getText(title.childNodes))

def handleSlideTitle(title):
    print("<h2>%s</h2>" % getText(title.childNodes))

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

def handlePoint(point):
    print("<li>%s</li>" % getText(point.childNodes))

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

handleSlideshow(dom)

20.7.3. minidom和DOM标准

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

在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 支持字节或字符串,但通常会产生字符串。 DOMString 类型的值也可以是 None,其中允许具有来自W3C的DOM规范的IDL null 值。

  • const 声明映射到它们各自范围内的变量(例如 xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE);它们不能改变。

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

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

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

  • DOMTimeStamp

  • DocumentType

  • DOMImplementation

  • CharacterData

  • CDATASection

  • Notation

  • Entity

  • EntityReference

  • DocumentFragment

大多数这些都反映在XML文档中的信息,这对大多数DOM用户来说不是通用的。

脚注

[1]

XML输出中包含的编码名称应符合相应的标准。例如,“UTF-8”有效,但是“UTF8”在XML文档的声明中无效,即使Python接受它作为编码名称。见 https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDeclhttps://www.iana.org/assignments/character-sets/character-sets.xhtml