Search code examples
xml-parsingtcltdom

Issues parsing xml data with TDOM


Hoping someone can point me in the right direction.

I'm relatively new to TCL but example code is very lacking compared to other languages I'm use to (Javascript/VbScript/C#).

Code:


package require http

package require tdom

set html "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:InfoResponse xmlns:u=\"urn:schemas-upnp-org:service:Transport:1\"><MyData>This is my final bit of data</MyData></u:InfoResponse></s:Body></s:Envelope>"


set doc [dom parse $html]

set root [$doc documentElement]

set node [$root selectNodes "/s:Envelope/s:Body/u:InfoResponse/MyData/text()"] 

puts [$node data]

The error is:

Prefix doesn't resolve
    while executing
"$root selectNodes "/s:Envelope/s:Body/u:InfoResponse/MyData/text()"
    invoked from within
"set node [$root selectNodes "/s:Envelope/s:Body/u:InfoResponse/MyData/text()]"

If I alter the XML and rename the elements to Envelope, Body etc it works fine (however not a possible solution as this is a datafeed). I have to confess my XML parsing skills are a bit lacking.

I tried an online xQuery tool and that parsed it ok. I'm guessing it might have something to do with the namespaces and TCL but not sure what to do..

I essentially only need to pull back the <MyData> from this bit of data and there will only be one response so there may be an easier way instead of xQuery? Some of the other data I will need to pull back will have multiple so it would be great to know how to do that!


Solution

  • Contrary to what you might be expecting, tDOM's selectNodes method doesn't use the namespace prefix mapping from the context node; you have to supply that mapping yourself with the -namespaces option, as documented.

    Try this:

    set nsmap {
        s  http://schemas.xmlsoap.org/soap/envelope/
        u  urn:schemas-upnp-org:service:Transport:1
    }
    set node [$root selectNodes -namespaces $nsmap \
            "/s:Envelope/s:Body/u:InfoResponse/MyData/text()"] 
    

    I'd worry a bit about the mapping for the unprefixed element, but it might work if it is left out of the mapping. (It's not normally a good idea to mingle namespaced an un-namespaced XML in the same document.)