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

   Return a "Document" that represents the *string*. This method
   creates a "StringIO" object for the string and passes that on to
   "parse()".

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

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

You can also create a "Document" by calling a method on a 「DOM
Implementation」 object.  You can get this object either by calling
the "getDOMImplementation()" function in the "xml.dom" package or the
"xml.dom.minidom" module. Using the implementation from the
"xml.dom.minidom" module will always return a "Document" instance from
the minidom implementation, while the version from "xml.dom" may
provide an alternate implementation (this is likely if you have the
PyXML package installed).  Once you have a "Document", you can add
child nodes to it to populate the 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 に関する勧告。


DOM オブジェクト
================

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

Node.unlink()

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

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

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

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

   バージョン 2.1 で変更: The optional keyword parameters *indent*,
   *addindent*, and *newl* were added to support pretty output.

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

Node.toxml([encoding])

   Return the XML that the DOM represents as a string.

   With no argument, the XML header does not specify an encoding, and
   the result is Unicode string if the default encoding cannot
   represent all characters in the document. Encoding this string in
   an encoding other than UTF-8 is likely incorrect, since UTF-8 is
   the default encoding of XML.

   With an explicit *encoding* [1] argument, the result is a byte
   string in the specified encoding. It is recommended that this
   argument is always specified. To avoid "UnicodeError" exceptions in
   case of unrepresentable text data, the encoding argument should be
   specified as 「utf-8」.

   バージョン 2.3 で変更: the *encoding* argument was introduced; see
   "writexml()".

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

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

   バージョン 2.1 で追加.

   バージョン 2.3 で変更: the encoding argument was introduced; see
   "writexml()".

The following standard DOM methods have special considerations with
"xml.dom.minidom":

Node.cloneNode(deep)

   Although this method was present in the version of
   "xml.dom.minidom" packaged with Python 2.0, it was seriously
   broken.  This has been corrected for subsequent releases.


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)


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 int" 、 "unsigned int" 、 "unsigned long long" 、および
  "boolean" 型は、全て Python 整数オブジェクトに対応付けられます。

* The type "DOMString" maps to Python strings. "xml.dom.minidom"
  supports either byte or Unicode strings, but will normally produce
  Unicode strings. Values of type "DOMString" may also be "None" where
  allowed to have the IDL "null" value by the DOM specification from
  the W3C.

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

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

* "NodeList" objects are implemented using Python’s built-in list
  type. Starting with Python 2.2, these objects provide the interface
  defined in the DOM specification, but with earlier versions of
  Python they do not support the official API.  They are, however,
  much more 「Pythonic」 than the interface defined in the W3C
  recommendations.

以下のインタフェースは "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] The encoding string included in XML output should conform to
    the appropriate standards. For example, 「UTF-8」 is valid, but 「
    UTF8 」 is not. See https://www.w3.org/TR/2006/REC-
    xml11-20060816/#NT- EncodingDecl and
    https://www.iana.org/assignments/character-sets /character-
    sets.xhtml.
