From: Phrogz Date: 2008-01-24T13:00:05+09:00 Subject: Re: Parsing poorly written XML On Jan 23, 5:08 pm, Patrick Callahan wrote: > Gavin Kistner wrote: > Wow!  That worked on the first try!  I knew there had to be a way, but > I'm trying to whip this script out and I didn't want to spend a week > studying XML parsing options in Ruby to do it.  You've saved me a bunch > of effort. Now I just need to figure out how to gracefully bail out > and continue on with the rest of the script if "foo" isn't there at all. > Uhm... I seem to be having trouble with that piece too.  I'm sure it's > easier than the first problem, but maybe it's just 'cause my brain is > cooked right now... The first piece of code I wrote has a potential to be cranky. doc.each_element( "//item[name='foo']" ){ |item| puts item.get_elements( "value" ).first.text } If no ...foo elements can be found, the block will never be called. However, if it finds such an element, but the doesn't have a child, then item.get_elements( "value" ) will return an empty array. Calling .first on an empty array will return nil, and then you'll have an error when you try to run the 'text' method of nil. You could modify the above code to guard for this: doc.each_element( "//item[name='foo']" ){ |item| first_value = item.get_elements( "value" ).first if first_value puts first_value.text end } However, the second (single XPath query) solution I wrote is both shorter and also more fault tolerant. If you assume that there's only one that will match the foo criterion, then you can change from REXML::XPath.match (which returns an array, possibly empty) to REXML::XPath.first (which returns either a single value, or nil). Something like: value = REXML::XPath.first( doc, "//item[name='foo']/value/text()" ) if value # do my stuff here end Or, to use your exact case: # Returns the uninstall command run (if found), # or nil if the necessary info couldn't be found. def uninstall_product( xml_string, prod_name ) require 'rexml' include REXML uninstall_command = XPath.first( Document.new( xml_string ), "//item[product_name='#{prod_name}']/uninstall_string/text()" ) if uninstall_command # Do the uninstall; maybe as simple as: `#{uninstall_command}` else warn "Either product '#{prod_name}' doesn't exist, " + "or it doesn't have an uninstall string." end uninstall_command end