I'm using the xml-object gem to process XML. Certain elements in the XML we receive may or may not be repeated. When they only appear once, xml-object binds them as a non-array property, but when they appear more than once they're bound as an array. I would prefer to treat them as if they were always bound as an array. This should explain what I'm talking about:
!/usr/bin/env ruby
require 'rubygems'
require 'xml-object'
XML_ONE = <<END
<foo>
<bar baz="123">abc</bar>
</foo>
END
foo = XMLObject.new(XML_ONE)
puts foo.bar
puts foo.bar.baz
XML_TWO = <<END
<foo>
<bar baz="123">abc</bar>
<bar baz="456">def</bar>
</foo>
END
foo = XMLObject.new(XML_TWO)
puts foo.bar[0]
puts foo.bar[0].baz
What I would like to do is process xml in the form of XML_ONE identically to xml of the form XML_TWO. I tried doing this:
puts [*foo.bar][0]
puts foo.bar.to_a[0]
puts [*foo.bar][0].baz
puts foo.bar.to_a[0].baz
The first two lines output "abc". The second two fail, complaining that there's no method "baz" on "abc". From what I can tell, when the result of "foo.bar" is coerced into an array, that array contains a "plain" string and not the instrumented string returned by "foo.bar", which has the method "baz".
Any thoughts?
I would try to use something different to parse the XML. After a bit of playing with the xml-object gem, it seems to lack consistency with the type of objects that it returns. I would use Nokogiri
instead. I was able to get the behavior you were trying for with this code:
#!/usr/bin/env ruby
require 'rubygems'
require 'xml-object'
require 'nokogiri'
XML_ONE = <<END
<foo>
<bar baz="123">abc</bar>
</foo>
END
XML_TWO = <<END
<foo>
<bar baz="123">abc</bar>
<bar baz="456">def</bar>
</foo>
END
xml1 = Nokogiri::XML(XML_ONE)
xml2 = Nokogiri::XML(XML_TWO)
puts xml1.xpath('/foo/bar/@baz')[0]
puts xml2.xpath('/foo/bar/@baz')[1]
Results:
123
456
There is similar functionality to XMLObject in Nokogiri as well, via Nokogiri::Slop
, but it was just as inconsistent as XMLObject, which is why I opted to use Nokogiri::XML().xpath
instead.