
ここでは Tcl/Tk を使って XML 文書の DOM ツリーを表示する短いプログラムを作成し、解説していきます。 XML の解析には TclDOM を、ツリー表示には BWidget に含まれる Tree ウィジェットを使います。 これらの拡張は ActiveTcl のパッケージには両方とも含まれています。
TclDOM は Tcl から DOM を扱うための拡張で、 XML 文書をツリー構造として扱うことができるようになります。 特徴として Tcl だけで実装された XML パーサを使うことが出来るため、 ポータビリティが高いということがあげられます。 TclDOM は DOM Level 1 (と Level 2, 3 の一部)を実装していますが、 TclDOM のリファレンスには DOM API 自体の説明が乏しいので、 必要に応じて DOM Level 1 specification を参照するとよいでしょう。 日本語版の DOM 1 仕様書を利用することも出来ます。
comp.lang.tcl のスレッド TclDOM2.4: Any issues compared with 2.0 によると、 2002-12-27 の時点で ActiveTcl の最新版として配布されている ActiveTcl 8.3.5.0 と ActiveTcl 8.4.1.0 に含まれる TclDOM はバグを含んでおり、このため当ページのプログラムも実行できません。 このページのプログラムは Windows 用の ActiveTcl 8.3.4.4 および Linux 用の ActiveTcl 8.3.4.3 に含まれる TclDOM で動作を確認しています。
今回作成したサンプルプログラムは以下のようなものです。
package require dom
package require BWidget
menu .menu
.menu add command -label {Open} -command {
global id
set filename [tk_getOpenFile -filetypes {{{XML Files} {.xml}}}]
if {$filename != ""} {
set f [open $filename r]
fconfigure $f -encoding utf-8
set doc [dom::DOMImplementation parse [read $f]] ;# XML の解析
close $f
dom::DOMImplementation trim $doc ;# トリム
set id 0
.tree delete [.tree nodes root] ;# Tree ウィジェットの初期化
addnodes root [dom::document cget $doc -documentElement]
}
}
. configure -menu .menu
Tree .tree -width 50 -height 30 \
-xscrollcommand {.sh set} -yscrollcommand {.sv set}
scrollbar .sv -command {.tree yview} -orient vertical
scrollbar .sh -command {.tree xview} -orient horizontal
grid .tree -row 0 -column 0 -sticky news
grid .sv -row 0 -column 1 -sticky ns
grid .sh -row 1 -column 0 -sticky we
grid columnconfigure . 0 -weight 1
grid rowconfigure . 0 -weight 1
proc addnodes {parent node} {
global id
set this [incr id]
set nodevalue [dom::node cget $node -nodeValue]
set nodename [dom::node cget $node -nodeName]
switch $nodename {
#text {
set label $nodevalue
regsub -all {\s+} $label { } label
set color black
}
default {
set label $nodename
set color red
}
}
.tree insert end $parent $this -text $label -open 1 -fill $color
foreach {name value} [array get [dom::node cget $node -attributes]] {
.tree insert end $this [incr id] \
-text "$name=\"$value\"" -open 1 -fill blue
}
if {[dom::node cget $node -nodeName] == "#text"} return
set child [dom::node cget $node -firstChild]
while {$child != ""} {
addnodes $this $child
set child [dom::node cget $child -nextSibling]
}
}
BWidget に含まれるウィジェットは package require BWidget とすることで使用できるようになります。
Tree ウィジェットは標準の Tk ウィジェットと同様に
Tree pathName ?option value...?
で作ることができ、その後 pack や grid などでレイアウトします。
使えるオプションの詳細は BWidgets のドキュメントを参照してください。
今回のサンプルプログラムではウィジェットのサイズとスクロールバーとの結び付けを指定しています。
Tree ウィジェット内の全てのノードには名前が付けられますが、 最上位のノードにはあらかじめ root という名前が付けられています(このノードは表示されません)。
ツリーにノードを追加するには以下の書式を使います。
pathName insert indexparentnode ?option value...?
parent で親ノードの名前を、 node で新しく追加する子ノードの名前を与えます。
index で親ノードの子リストの何番目に新しい子ノードを追加するかを指定します。
先頭に追加するなら 0 、末尾に追加するなら end を指定します。
option value... で指定できるオプションのうち、
今回のプログラムで使用したものは以下の三つです。
-text-open1 、閉じた状態で表示するなら 0 を指定します。-filldom::DOMImplementation parse data
このコマンドの引数 data に XML テキストを与えると DOM ツリーが作られ、 戻り値としてルートノードのトークンが返されます。
サンプルプログラムでは XML を解析したあと、
dom::DOMImplementation trim $doc というコマンドを実行しているのですが、
これはホワイトスペースのみからなるテキストノードを取り払うコマンドです。
例として以下の二つの XML 文書を比較して考えましょう。
<root><foo/></root>
<root>
<foo/>
</root>
前者の XML から DOM ツリーを作った場合、 root 要素の子ノードは foo 要素のみになります。
一方後者のようなタグが改行で区切られた XML の場合、 root の子ノードは 順番に、
改行のみのテキストノード、 foo 要素、改行のみのテキストノード、となります。
dom::DOMImplementation trim token というコマンドを通しておくと、
後者の場合も前者と同じ DOM ツリーが得られるわけです。
addnodes プロシージャは第一引数で指定した名前の Tree ウィジェットのノードの下に
第二引数で指定したノードを追加します。
第一引数の parent には Tree ノード内の名前を、第二引数の node には TclDOM のトークンを指定します。
node がさらに子を持っている場合は addnodes の中で再帰的に addnodes を呼び出します。
Tree ウィジェット内のノード名が重複しないようにグローバル変数 id を一つずつ増やし、 それを名前として使っています。
ノードがテキストノードの場合は黒でそのテキストの値を表示し、 要素の場合は赤で要素名を表示します。 要素が属性を持っている場合は青でその要素の子として表示します。
このプログラムで rei harakami online の RSS ファイルを表示した例は以下のようになります。
