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

バージョン 2.0 で追加.

ソースコード: 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[, bufsize]])

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

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

xml.dom.minidom.parseString(string[, parser])

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

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

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

"DOM 実装" オブジェクトのメソッドを呼び出して Document を生成することもできます。このオブジェクトは、 xml.dom パッケージ、または xml.dom.minidom モジュールの getDOMImplementation() 関数を呼び出して取得できます。 xml.dom.minidom モジュールの実装を使うと、常に minidom 実装の Document インスタンスを返します。一方、 xml.dom 版の関数では、別の実装によるインスタンスを返すかもれません (PyXML package がインストールされているとそうなるでしょう)。 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 特有の拡張です。ノードに対して unlink() を呼び出した後は、ノードとその下位ノードは本質的には無意味なものとなります。このメソッドを呼び出さなくても、 Python のガベージコレクタがいつかはツリーのオブジェクトを後片付けします。

参考

Document Object Model (DOM) Level 1 Specification
xml.dom.minidom でサポートされている W3C の DOM に関する勧告。

19.9.1. DOM オブジェクト

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

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

Node.writexml(writer, indent="", addindent="", newl="")

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

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

バージョン 2.1 で変更: 美しい出力をサポートするため、新たなキーワード引数 indentaddindent 、および newl が追加されました.

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

Node.toxml([encoding])

DOM が表現している XML を文字列にして返します。

引数がなければ、 XML ヘッダは encoding を指定せず、文書内の全ての文字をデフォルトエンコード方式で表示できない場合、結果は Unicode 文字列となります。この文字列を UTF-8 以外のエンコード方式でエンコードするのは不正であり、なぜなら UTF-8 が XML のデフォルトエンコード方式だからです。

明示的な encoding [1] 引数があると、結果は指定されたエンコード方式によるバイト文字列となります。引数を常に指定するよう推奨します。表現不可能なテキストデータの場合に UnicodeError が送出されるのを避けるため、encoding 引数は "utf-8" に指定するべきです。

バージョン 2.3 で変更: encoding が追加されました。 writexml() を参照して下さい。

Node.toprettyxml(indent="\t", newl="\n", encoding=None)

文書の整形されたバージョンを返します。 indent はインデントを行うための文字で、デフォルトはタブです; newl には行末で出力される文字列を指定し、デフォルトは \n です。

バージョン 2.1 で追加.

バージョン 2.3 で変更: encoding 引数が追加されました。 writexml() を参照して下さい。

以下の標準 DOM メソッドは、 xml.dom.minidom では特別な注意をする必要があります:

Node.cloneNode(deep)

このメソッドは Python 2.0 にパッケージされているバージョンの xml.dom.minidom にはありましたが、これには深刻な障害があります。以降のリリースでは修正されています。

19.9.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)

19.9.3. 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 ではバイト文字列 (byte string) および Unicode 文字列のどちらかに対応づけられますが、通常 Unicode 文字列を生成します。 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 の組み込みリスト型を使って実装されています。 Python 2.2 からは、これらのオブジェクトは DOM 仕様で定義されたインタフェースを提供していますが、それ以前のバージョンの Python では、公式の API をサポートしていません。しかしながら、これらの API は W3C 勧告で定義されたインタフェースよりも "Python 的な" ものになっています。

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

  • DOMTimeStamp
  • DocumentType (added in Python 2.1)
  • DOMImplementation (added in Python 2.1)
  • CharacterData
  • CDATASection
  • Notation
  • Entity
  • EntityReference
  • DocumentFragment

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

注記

[1]XML 出力に含まれるエンコーディング文字列は適切な規格に従っていなければなりません。例えば、 "UTF-8" は有効ですが、 "UTF8" はそうではありません。https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDeclhttps://www.iana.org/assignments/character-sets/character-sets.xhtml を参照してください。