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

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

======================================================================

"xml.dom.minidom" is a minimal implementation of the Document Object
Model interface, with an API similar to that in other languages.  It
is intended to be simpler than the full DOM and also significantly
smaller.  Users who are not already proficient with the DOM should
consider using the "xml.etree.ElementTree" module for their XML
processing instead.

注釈:

  If you need to parse untrusted or unauthenticated data, see XML
  security.

DOM applications typically start by parsing some XML into a DOM.  With
"xml.dom.minidom", this is done through the parse functions:

   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" オブジェクトを
返します。

What the "parse()" and "parseString()" functions do is connect an XML
parser with a "DOM builder" that can accept parse events from any SAX
parser and convert them into a DOM tree.  The names of the functions
are perhaps misleading, but are easy to grasp when learning the
interfaces.  The parsing of the document will be completed before
these functions return; it's simply that these functions do not
provide a parser implementation themselves.

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

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
     The W3C recommendation for the DOM supported by
     "xml.dom.minidom".


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

The definition of the DOM API for Python is given as part of the
"xml.dom" module documentation.  This section lists the differences
between the API and "xml.dom.minidom".

Node.unlink()

   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 標準
===================

The "xml.dom.minidom" module is essentially a DOM 1.0-compatible DOM
with some DOM 2 features (primarily namespace features).

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 bytes or strings, but will normally produce 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" is currently not supported in "xml.dom.minidom".
  Instead, "xml.dom.minidom" uses standard Python exceptions such as
  "TypeError" and "AttributeError".

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

The following interfaces have no implementation in "xml.dom.minidom":

* "DOMTimeStamp"

* "EntityReference"

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

-[ 脚注 ]-

[1] XML 出力に含まれるエンコード名は適切な規格に従っていなければなりま
    せん。例えば "UTF-8" は有効ですが、 "UTF8" は XML 文書の宣言では有
    効ではありません。後者はエンコード名として Python に認められるとし
    てもです。https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-
    EncodingDecl と https://www.iana.org/assignments/character-sets
    /character-sets.xhtml を参照してください。
