views:

125

answers:

2

I'm using tDom to loop through some XML and pull out each element's text().

    set xml {
<systems>
 <object>
  <type>Hardware</type>
  <name>Server Name</name>
  <attributes>
   <vendor></vendor>
  </attributes>
 </object>
 <object>
  <type>Hardware</type>
  <name>Server Two Name</name>
  <attributes>
   <vendor></vendor>
  </attributes>
 </object>
</systems>
};

    set doc  [dom parse $xml]
    set root [$doc documentElement]

    set nodeList [$root selectNodes /systems/object]

    foreach node $nodeList {

     set nType  [$node selectNodes type/text()]
     set nName  [$node selectNodes name/text()]
     set nVendor [$node selectNodes attributes/vendor/text()]

     # Etc...
     puts "Type: " 
     puts [$nType data] 

     # Etc ..

     puts [$nVendor data]
    }

But when it tries to print out the Vendor, which is empty, it thows the error invalid command name "". How can I ignore this and just set $nVendor to an empty string?

+2  A: 

You can check to see if $nVendor is an empty node:

if {$nVendor != ""} {
    puts [$nVendor data]
}
Hai Vu
better to use `ne`
Donal Fellows
Donal: Thank you for that guideline, I now know that ne,eq... should be used for string comparisons and !=,== for numerical.
Hai Vu
+3  A: 

The selectNodes method of a node returns a list of nodes that match your pattern. When you use the results directly as a command

set nName  [$node selectNodes name/text()]
puts [$nType data] 

what you are really doing is taking advantage of the fact that a list of 1 item (the number of name items) is the same as one item. When there are no matching nodes, you get back an empty list

set nVendor [$node selectNodes attributes/vendor/text()]  ;# -> {}

and, when you call that, it's throwing an error because you're calling a command with the name {}.

set nVendor [$node selectNodes attributes/vendor/text()]  ;# -> {}
puts [$nVendor data]  ;# -> winds up calling
{} data

As noted by Hai Vu, you can test that there was a result by checking the result against "". A "more correct" way would probably be to check it against the empty list

set nVendor [$node selectNodes attributes/vendor/text()]
if {[llength $nVendor] == 1} {
    puts [$nVendor data]
}

or, to be even more complete (if you're not sure about the input XML)

set nVendor [$node selectNodes attributes/vendor/text()]
switch -exact -- [llength $nVendor] {
    0 { 
        # no nVendor, do nothing
    }
    1 {
        # 1 vendor, print it
        puts [$nVendor data]
    }
    default {
        # weird, we got more than one vendor node... throw an error
        error "More than one vendor node"
    }
}
RHSeeger
If you write the XPath slightly differently, you can guarantee that there will be exactly 0 or 1 value in the result list and using `llength` to test then is a trivial op.
Donal Fellows