xml.dom.minidom --- 最小限の DOM の実装

ソースコード: Lib/xml/dom/minidom.py


xml.dom.minidom は、 Document Object Model インターフェースの最小の実装です。他言語の実装と似た API を持ちます。このモジュールは、完全な DOM に比べて単純で、非常に小さくなるように意図されています。 DOM について既に熟知しているユーザを除き、 XML 処理には代わりに xml.etree.ElementTree モジュールを使うことを検討すべきです。

警告

xml.dom.minidom モジュールは悪意を持って作成されたデータに対して安全ではありません。信頼できないデータや認証されていないデータをパースする必要がある場合は XML の脆弱性 を参照してください。

DOM アプリケーションは通常、XML を DOM に解析 (parse) することで開始します。 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)

与えられた入力から Document を返します。 filename_or_file はファイル名でもファイルオブジェクトでもかまいません。 parser を指定する場合、SAX2 パーザオブジェクトでなければなりません。この関数はパーザの文書ハンドラを変更し、名前空間サポートを有効にします; (エンティティリゾルバ (entity resolver) のような) 他のパーザ設定は前もっておこなわなければなりません。

XML データを文字列で持っている場合、 parseString() を代わりに使うことができます:

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

string を表わす Document を返します。このメソッドは、文字列に対する io.StringIO オブジェクトを作成し、それを parse() に渡します。

これらの関数は両方とも、文書の内容を表現する Document オブジェクトを返します。

parse()parseString() といった関数が行うのは、 XML パーザを、何らかの SAX パーザからくる解析イベント (parse event) を受け取って DOM ツリーに変換できるような "DOM ビルダ (DOM builder)" に結合することです。関数は誤解を招くような名前になっているかもしれませんが、インターフェースについて学んでいるときには理解しやすいでしょう。文書の解析はこれらの関数が戻るより前に完結します; 要するに、これらの関数自体はパーザ実装を提供しないということです。

"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"

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.

参考

Document Object Model (DOM) Level 1 Specification

xml.dom.minidom でサポートされている W3C の DOM に関する勧告。

DOM オブジェクト

Python の DOM API 定義は xml.dom モジュールドキュメントの一部として与えられています。この節では、 xml.dom の API と xml.dom.minidom との違いについて列挙します。

DOM との内部的な参照を破壊して、循環参照ガベージコレクションを持たないバージョンの Python でもガベージコレクションされるようにします。循環参照ガベージコレクションが利用できる場合でも、このメソッドを使えば大量のメモリをすぐに使えるようにできるため、不要になったらすぐに DOM オブジェクトに対してこのメソッドを呼ぶのが良い習慣です。このメソッドは Document オブジェクトに対して呼び出すだけでよいのですが、あるノードの子ノードを破棄するために子ノードに対して呼び出してもかまいません。

with ステートメントを使用することで、このメソッドを明示的に呼ばないようにできます。 with ブロックから出る時に自動的に次のコードが dom を unlink します:

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

XML を writer オブジェクトに書き込みます。 writer は入力としてテキストは受け付けますが、バイト列は受け付けません。 writer はファイルオブジェクトインターフェースの write() に該当するメソッドを持たなければなりません。 indent 引数には現在のノードのインデントを指定します。 addindent 引数には現在のノードの下にサブノードを追加する際のインデント増分を指定します。 newl には、改行時に行末を終端する文字列を指定します。

Document ノードでは、追加のキーワード引数 encoding を使って XML ヘッダの encoding フィールドを指定することができます。

同様に、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 宣言はエンコーディングを指定しません。XML のデフォルトエンコーディングは UTF-8 なので、この文字列を UTF-8 以外でエンコードすることはおそらく正しくありません。

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 オブジェクト上で利用可能な生成関数 (creator function) を使わなければなりません。派生インターフェースでは基底インターフェースの全ての演算 (および属性) に加え、新たな演算をサポートします。

  • 演算はメソッドとして使われます。DOM では in パラメタのみを使うので、引数は通常の順番 (左から右へ) で渡されます。オプション引数はありません。 void 演算は None を返します。

  • IDL 属性はインスタンス属性に対応付けられます。OMG IDL 言語における Python への対応付けとの互換性のために、属性 foo はアクセサメソッド _get_foo() および _set_foo() でもアクセスできます。 readonly 属性は変更してはなりません; とはいえ、これは実行時には強制されません。

  • short intunsigned intunsigned long long 、および boolean 型は、全て Python 整数オブジェクトに対応付けられます。

  • DOMString 型は Python 文字列型に対応付けられます。 xml.dom.minidom ではバイト列か文字列のどちらかに対応づけられますが、通常文字列を生成します。 DOMString 型の値は、W3C の DOM 仕様で、IDL null 値になってもよいとされている場所では None になることもあります。

  • const 宣言を行うと、 (xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE のように) 対応するスコープ内の変数に対応付けを行います; これらは変更してはなりません。

  • DOMException は現状では xml.dom.minidom でサポートされていません。その代わり、 xml.dom.minidom は、 TypeErrorAttributeError といった標準の Python 例外を使います。

  • NodeList オブジェクトは Python の組み込みのリスト型を使って実装されています。これらのオブジェクトは DOM 仕様で定義されたインターフェースを提供していますが、以前のバージョンの Python では、公式の API をサポートしていません。しかしながら、これらの API は W3C 勧告で定義されたインターフェースよりも "Python 的な" ものになっています。

以下のインターフェースは xml.dom.minidom では全く実装されていません:

  • DOMTimeStamp

  • EntityReference

これらの大部分は、ほとんどの DOM のユーザにとって一般的な用途として有用とはならないような XML 文書内の情報を反映しています。

脚注