xml.dom.minidom --- 最小化的 DOM 實(shí)現(xiàn)?

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


xml.dom.minidom 是文檔對(duì)象模型接口的最小化實(shí)現(xiàn),具有與其他語(yǔ)言類(lèi)似的 API。 它的目標(biāo)是比完整 DOM 更簡(jiǎn)單并且更為小巧。 對(duì)于 DOM 還不十分熟悉的用戶則應(yīng)當(dāng)考慮改用 xml.etree.ElementTree 模塊來(lái)進(jìn)行 XML 處理。

警告

xml.dom.minidom 模塊對(duì)于惡意構(gòu)建的數(shù)據(jù)是不安全的。 如果你需要解析不受信任或未經(jīng)身份驗(yàn)證的數(shù)據(jù),請(qǐng)參閱 XML 漏洞。

DOM 應(yīng)用程序通常會(huì)從將某個(gè) XML 解析為 DOM 開(kāi)始。 使用 xml.dom.minidom 時(shí),這是通過(guò)各種解析函數(shù)來(lái)完成的:

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() 函數(shù)可接受一個(gè)文件名或者打開(kāi)的文件對(duì)象。

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

根據(jù)給定的輸入返回一個(gè) Document。 filename_or_file 可以是一個(gè)文件名,或是一個(gè)文件類(lèi)對(duì)象。 如果給定 parser 則它必須是一個(gè) SAX2 解析器對(duì)象。 此函數(shù)將修改解析器的處理程序并激活命名空間支持;其他解析器配置(例如設(shè)置一個(gè)實(shí)體求解器)必須已經(jīng)提前完成。

如果你將 XML 存放為字符串形式,則可以改用 parseString() 函數(shù):

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

返回一個(gè)代表 stringDocument。 此方法會(huì)為指定字符串創(chuàng)建一個(gè) io.StringIO 對(duì)象并將其傳遞給 parse()。

兩個(gè)函數(shù)均返回一個(gè)代表文檔內(nèi)容的 Document 對(duì)象。object representing the content of the document.

parse()parseString() 函數(shù)所做的是將 XML 解析器連接到一個(gè) "DOM 構(gòu)建器",它可以從任意 SAX 解析器接收解析事件并將其轉(zhuǎn)換為 DOM 樹(shù)結(jié)構(gòu)。 這兩個(gè)函數(shù)的名稱(chēng)可能有些誤導(dǎo)性,但在學(xué)習(xí)此接口時(shí)是很容易掌握的。 文檔解析操作將在這兩個(gè)函數(shù)返回之前完成;簡(jiǎn)單地說(shuō)這兩個(gè)函數(shù)本身并不提供解析器實(shí)現(xiàn)。

你也可以通過(guò)在一個(gè) "DOM 實(shí)現(xiàn)" 對(duì)象上調(diào)用方法來(lái)創(chuàng)建 Document。 此對(duì)象可通過(guò)調(diào)用 xml.dom 包或者 xml.dom.minidom 模塊中的 getDOMImplementation() 函數(shù)來(lái)獲取。 一旦你獲得了一個(gè) Document,你就可以向它添加子節(jié)點(diǎn)來(lái)填充 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 文檔對(duì)象,你就可以通過(guò)其屬性和方法訪問(wèn)對(duì)應(yīng) XML 文檔的各個(gè)部分。 這些屬性定義在 DOM 規(guī)格說(shuō)明當(dāng)中;文檔對(duì)象的主要特征屬性是 documentElement。 它給出了 XML 文檔中的主元素:即包含了所有其他元素的元素。 以下是一個(gè)示例程序:

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

當(dāng)你完成對(duì)一個(gè) DOM 樹(shù)的處理時(shí),你可以選擇調(diào)用 unlink() 方法以鼓勵(lì)盡早清除不再需要的對(duì)象。 unlink()xml.dom.minidom 針對(duì) DOM API 的專(zhuān)屬擴(kuò)展,它會(huì)將特定節(jié)點(diǎn)及其下級(jí)標(biāo)記為不再有用。 在其他情況下,Python 的垃圾回收器將負(fù)責(zé)最終處理樹(shù)結(jié)構(gòu)中的對(duì)象。

參見(jiàn)

文檔對(duì)象模型 (DOM) 第 1 層級(jí)規(guī)格說(shuō)明

xml.dom.minidom 所支持的 W3C 針對(duì) DOM 的建議。

DOM 對(duì)象?

Python 的 DOM API 定義被作為 xml.dom 模塊文檔的一部分給出。 這一節(jié)列出了該 API 和 xml.dom.minidom 之間的差異。

破壞 DOM 的內(nèi)部引用以便它能在沒(méi)有循環(huán) GC 的 Python 版本上垃圾回收器回收。 即使在循環(huán) GC 可用的時(shí)候,使用此方法也可讓大量?jī)?nèi)存更快變?yōu)榭捎?,因此?dāng) DOM 對(duì)象不再被需要時(shí)盡早調(diào)用它們的這個(gè)方法是很好的做法。 此方法只須在 Document 對(duì)象上調(diào)用,但也可以在下級(jí)節(jié)點(diǎn)上調(diào)用以丟棄該節(jié)點(diǎn)的下級(jí)節(jié)點(diǎn)。

你可以通過(guò)使用 with 語(yǔ)句來(lái)避免顯式調(diào)用此方法。 以下代碼會(huì)在 with 代碼塊退出時(shí)自動(dòng)取消鏈接 dom:

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

將 XML 寫(xiě)入到寫(xiě)入器對(duì)象。 寫(xiě)入器接受文本而非字節(jié)串作為輸入,它應(yīng)當(dāng)具有與文件對(duì)象接口相匹配的 write() 方法。 indent 形參是當(dāng)前節(jié)點(diǎn)的縮進(jìn)層級(jí)。 addindent 形參是用于當(dāng)前節(jié)點(diǎn)的下級(jí)節(jié)點(diǎn)的縮進(jìn)量。 newl 形參指定用于一行結(jié)束的字符串。

對(duì)于 Document 節(jié)點(diǎn),可以使用附加的關(guān)鍵字參數(shù) encoding 來(lái)指定 XML 標(biāo)頭的編碼格式字段。

類(lèi)似地,顯式指明 standalone 參數(shù)將會(huì)使單獨(dú)的文檔聲明被添加到 XML 文檔的開(kāi)頭部分。 如果將該值設(shè)為 True,則會(huì)添加 standalone="yes",否則將為 "no"。 未指明該參數(shù)將使文檔聲明被省略。

在 3.8 版更改: writexml() 方法現(xiàn)在會(huì)保留用戶指定的屬性順序。

在 3.9 版更改: The standalone parameter was added.

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

返回一個(gè)包含 XML DOM 節(jié)點(diǎn)所代表的 XML 的字符串或字節(jié)串。

帶有顯式的 encoding 1 參數(shù)時(shí),結(jié)果為使用指定編碼格式的字節(jié)串。 沒(méi)有 encoding 參數(shù)時(shí),結(jié)果為 Unicode 字符串,并且結(jié)果字符串中的 XML 聲明將不指定編碼格式。 使用 UTF-8 以外的編碼格式對(duì)此字符串進(jìn)行編碼通常是不正確的,因?yàn)?UTF-8 是 XML 的默認(rèn)編碼格式。

standalone 參數(shù)的行為與 writexml() 中的完全一致。

在 3.8 版更改: toxml() 方法現(xiàn)在會(huì)保留用戶指定的屬性順序。

在 3.9 版更改: The standalone parameter was added.

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

返回文檔的美化打印版本。 indent 指定縮進(jìn)字符串并默認(rèn)為制表符;newl 指定標(biāo)示每行結(jié)束的字符串并默認(rèn)為 \n。

encoding 參數(shù)的行為類(lèi)似于 toxml() 的對(duì)應(yīng)參數(shù)。

standalone 參數(shù)的行為與 writexml() 中的完全一致。

在 3.8 版更改: toprettyxml() 方法現(xiàn)在會(huì)保留用戶指定的屬性順序。

在 3.9 版更改: The standalone parameter was added.

DOM 示例?

此示例程序是個(gè)相當(dāng)實(shí)際的簡(jiǎn)單程序示例。 在這個(gè)特定情況中,我們沒(méi)有過(guò)多地利用 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 標(biāo)準(zhǔn)?

xml.dom.minidom 模塊實(shí)際上是兼容 DOM 1.0 的 DOM 并帶有部分 DOM 2 特性(主要是命名空間特性)。

Python 中 DOM 接口的用法十分直觀。 會(huì)應(yīng)用下列映射規(guī)則:

  • 接口是通過(guò)實(shí)例對(duì)象來(lái)訪問(wèn)的。 應(yīng)用程序不應(yīng)實(shí)例化這些類(lèi)本身;它們應(yīng)當(dāng)使用 Document 對(duì)象提供的創(chuàng)建器函數(shù)。 派生的接口支持上級(jí)接口的所有操作(和屬性),并添加了新的操作。

  • 操作以方法的形式使用。 因由 DOM 只使用 in 形參,參數(shù)是以正常順序傳入的(從左至右)。 不存在可選參數(shù)。 void 操作返回 None。

  • IDL 屬性會(huì)映射到實(shí)例屬性。 為了兼容針對(duì) Python 的 OMG IDL 語(yǔ)言映射,屬性 foo 也可通過(guò)訪問(wèn)器方法 _get_foo()_set_foo() 來(lái)訪問(wèn)。 readonly 屬性不可被修改;運(yùn)行時(shí)并不強(qiáng)制要求這一點(diǎn)。

  • short int, unsigned int, unsigned long longboolean 類(lèi)型都會(huì)映射為 Python 整數(shù)類(lèi)型。

  • DOMString 類(lèi)型會(huì)映射為 Python 字符串。 xml.dom.minidom 支持字節(jié)串或字符串,但通常是產(chǎn)生字符串。 DOMString 類(lèi)型的值也可以為 None,W3C 的 DOM 規(guī)格說(shuō)明允許其具有 IDL null 值。

  • const 聲明會(huì)映射為它們各自的作用域內(nèi)的變量 (例如 xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE);它們不可被修改。

  • DOMException 目前不被 xml.dom.minidom 所支持。 xml.dom.minidom 會(huì)改為使用標(biāo)準(zhǔn) Python 異常例如 TypeErrorAttributeError。

  • NodeList 對(duì)象是使用 Python 內(nèi)置列表類(lèi)型來(lái)實(shí)現(xiàn)的。 這些對(duì)象提供了 DOM 規(guī)格說(shuō)明中定義的接口,但在較早版本的 Python 中它們不支持官方 API。 相比在 W3C 建議中定義的接口,它們要更加的 "Pythonic"。

下列接口未在 xml.dom.minidom 中實(shí)現(xiàn):

  • DOMTimeStamp

  • EntityReference

這些接口所反映的 XML 文檔信息對(duì)于大多數(shù) DOM 用戶來(lái)說(shuō)沒(méi)有什么幫助。

備注

1

包括在 XML 輸出中的編碼格式名稱(chēng)應(yīng)當(dāng)遵循適當(dāng)?shù)臉?biāo)準(zhǔn)。 例如,"UTF-8" 是有效的,但 "UTF8" 在 XML 文檔的聲明中是無(wú)效的,即使 Python 接受其作為編碼格式名稱(chēng)。 詳情參見(jiàn) https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDeclhttps://www.iana.org/assignments/character-sets/character-sets.xhtml。