libxml2modでDOM3

サンプルコード

最終更新
2004-08-11T20:30:44+09:00

突然ですがサンプルを。DOM Level 3 Core、Load and Saveの仕様書はおさえておく必要があります。

import DOMImplementationSource as src

impl = src.getDOMImplementation("LS 3.0 MS")
parser = impl.createLSParser(1, "http://relaxng.org/ns/structure/1.0")
config = parser.domConfig
config.setParameter("schema-location", "xhtml1-transitional.rng")
config.setParameter("xml-catalog", "mycatalog.xml")
config.setParameter("validate", True)
doc = parser.parseURI("test.xhtml")

doc.xpathNSMap["xht"] = "http://www.w3.org/1999/xhtml"
nl = doc.selectNodes(
    "descendant::xht:*/@*[name() = 'src' or name() = 'href']")
for att in nl:
    print att.value

config.setParameter("validate", False)
styleDoc = parser.parseURI("xhtml_to_rss.xsl")
xslt = impl.createXSLTStylesheet(styleDoc)
resultDoc = xslt.transformDocument(doc, None)

serializer = impl.createLSSerializer()
serializer.writeToURI(resultDoc, "test.rss")

導入

最終更新
2004-08-11T20:30:44+09:00

これは何

libxml2modを使って、W3C DOM Level 3 Core、LS、XPath等 の実装を作ってみよう……としました。わらい。

動作環境

Python 2.3、libxml2(The XML C parser and toolkit of Gnome)が必要です。

インストール

インストールは適当にやってください。良く分からない場合おっかないから止めておいてください。

何故

最終更新
2004-08-11T21:58:33+09:00

わざわざ苦労してこんな事をするからには、それなりに訳があります。

  • 面白いから
  • 標準フェチだから
  • コードの保守性が高まるから

DOM Level 3ではブートストラッピング(実装の取得)が仕様に盛り込まれました。今まで全コード共通のブートストラッピングに関しては自分で勝手にいろいろやっていたので、このインターフェイス(DOMImplementationSource)で書き直そうと思ったのがそもそものきっかけです。

注意点など

最終更新
2004-08-11T20:30:44+09:00

バグは至る所にあります。特にメモリリークには十分に気を付ける必要があります。

先のサンプルコードの場合、関数内に入れて呼び出す形にすると、各変数の参照カウントが減って、libxml2のfree系のメソッド(freeDoc()等)が自動で呼ばれ(るように工夫してい)ます。とはいえ必ず次のようなコードで確認する必要があります。

import libxml2, libxslt
libxml2.debugMemory(1)

コードを記述

libxml2.cleanupParser()
libxslt.cleanup()
if libxml2.debugMemory(1) == 0:
    print "No memory leak"
else:
    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
    libxml2.dumpMemory()

メモリを解放するメソッドと引数に与えるPyObjectを参照している属性を列挙:

libxml2mod.xmlXPathFreeContext
NodeList.__context
libxml2mod.xmlFreeDoc
Document._o
libxml2mod.xmlRelaxNGFreeParserCtxt
LSParser.__relaxNGStuff[0]
libxml2mod.xmlRelaxNGFree
LSParser.__relaxNGStuff[1]
libxml2mod.xmlRelaxNGFreeValidCtxt
LSParser.__relaxNGStuff[2]
libxml2mod.xmlRelaxNGCleanupTypes
(引数無し)
libxml2mod.htmlFreeParserCtxt
LSParser.parseメソッド内部で使用(参照無し)
libxsltmod.xsltFreeStylesheet
XSLTStylesheet._o
libxml2mod.xmlFreeTextReader
XmlTextReader._o

XPathを用いたノードリストの取得とXSLTによる変換に関しては、独自拡張インターフェイスにしました。feature名「MS」で実装を取得します。メソッドやアトリビュートの詳細はインタープリタでhelp()か何かしてください。

libxml2のデータモデルが無茶なので、実体参照は置き換えます。実体参照大好きっ子は勘弁してください。因みに私は実体参照が大嫌いです。

libxml2modを利用しているので、libxml2で出来ないことは、出来ません。出来ることも、出来ない場合があります。そのような場合はlibxml2のAPIを使用してください。

libxml2modが返したPyObjectにDOMのインターフェイスを被せてノードを生成する為、ノードの同一性の確認が苦手です。isSameNodeメソッドが唯一の手段ですが、かなり非効率になっています。

NodeList、NamedNodeMapを「生きた」状態に保つ為、内部的にXPathを使用しています(手抜き)(因みに、参考にする為ソースコードを読んでみたところ、xml.dom.minidomモジュールのそれらは「死んで」いました)。そのため、ノードを検索して処理といった作業にはXmlTextReaderインターフェイスを使った方が「省エネ」です。このインターフェイスはMSモジュールで定義しており「工場」DOMImplementationMS の createXmlTextReaderメソッドでオブジェクトを作成可能です。XmlTextReaderインターフェイスについてはLibxml2 XmlTextReader Interface tutorial 参照。

DOMConfigurationの各パラメータには、殆ど対応していません。値を変更可能かどうかはcanSetParameterメソッドで確認してください。以下変更できる主なパラメタ:

validate
Trueで妥当性検証あり、Falseで妥当性検証なし
schema-type
スキーマ文書の種類をURIで指定(http://www.w3.org/TR/REC-xml でXML DTD、http://relaxng.org/ns/structure/1.0 でRELAX NG)
schema-location
スキーマ文書のURIを指定
xml-catalog (独自拡張パラメタ)
XML CatalogのURIを指定

ToDo

最終更新
2004-08-12T14:04:56+09:00
  • DOMErrorHandlerやDOMError辺りを使えるように何とかする