MJ The following code defines an xml::inspect command which will create a Tk window with an XML document. The XML document can be drilled down into with the tree view or a specific view can be created by putting an xpath expression in the entry field at the bottom.
#!/usr/bin/env wish
namespace eval xml {
proc inspect {xml title} {
package require Tk
package require tdom
# parse XML
set doc [dom parse $xml]
set nsprefixlist {}
foreach {item} [$doc selectNodes {//namespace::*}] {
lassign $item fullprefix uri
lassign [split $fullprefix :] _ prefix
lappend nsprefixlist $prefix $uri
}
# build ui
set tl [toplevel .$doc]
wm protocol $tl WM_DELETE_WINDOW [namespace code [list cleanup $doc $tl]]
ttk::panedwindow $tl.pane -orient horizontal
ttk::treeview $tl.tv -selectmode extended -show tree
bind $tl.tv <<TreeviewSelect>> [namespace code [list updateText $tl.txt %W]]
text $tl.txt
fillTree $tl.tv [list [$doc documentElement]]
$tl.pane add $tl.tv -weight 1
$tl.pane add $tl.txt -weight 2
entry $tl.entry
pack $tl.pane -expand 1 -fill both
pack $tl.entry -expand 0 -fill x
bind $tl.entry <Return> [namespace code [list evaluateXPath $doc $nsprefixlist %W $tl.tv]]
if {$title ne {}} {
wm title $tl $title
}
return $tl
}
proc evaluateXPath {doc nsprefixlist xpathWidget tv} {
set xpath [$xpathWidget get]
if {$xpath eq {} } {
set nodes [list [$doc documentElement]]
} else {
set nodes [$doc selectNodes -namespaces $nsprefixlist $xpath]
}
fillTree $tv $nodes
}
proc cleanup {doc tl} {
$doc delete
destroy $tl
}
proc fillTree {tv nodes} {
# puts stderr "Adding [llength $nodes] nodes"
foreach item [$tv children {}] {
$tv detach $item
}
$tv selection remove [$tv selection]
if {[catch {addNodes $tv {} $nodes } result]} {
# Just add the text of the XPath result if it's not a list of nodes.
# Call it in a callback because the clearing of the selection will clear the txt view.
puts stderr $result
after 0 [list [winfo parent $tv].txt insert end [join $nodes \n]]
}
}
proc updateText {txt lv} {
$txt delete 1.0 end
foreach node [$lv selection] {
$txt insert end [$node asXML]
$txt insert end \n
}
}
proc addNodes {tv parent nodes} {
set index 0
# puts stderr $parent-$nodes
foreach node $nodes {
set name [$node nodeName]
if {$name eq "#text"} {
continue
}
if [$tv exists $node] {
$tv move $node $parent $index
} else {
$tv insert $parent $index -id $node -text $name
}
set children [$node childNodes]
# puts stderr "Children -> $children"
if {$children ne {}} {
addNodes $tv $node $children
}
incr index
}
}
}
# Example usage to open an XML file
set file [lindex $argv 0]
set f [open $file]
set xml [read $f]
close $f
xml::inspect $xml $file