Last year I made a quick hack that could do something like that.
I didn't have much experience with Ruby back then, so it is a quick and
very dirty solution. But the following works:
require 'XML'
y = XML %{
<employees>
<employee>
<name>Joe</name>
<age>28</age>
<department>
<name>Engineering</name>
</department>
</employee>
<employee>
<name>Ken</name>
<age>26</age>
<department>
<name>Engineering</name>
</department>
</employee>
</employees>
}
puts y.employees.employee[0].age
y.employees.employee[0].age = '9'
puts y.employees.employee[0].age
y.employees.employee.each do |employee|
puts "name: #{employee.name}, age: #{employee.age}"
end
doc = XML %{
<order>
<customer>
<name>I. Wannabuy</name>
<address>
<street>53 Party Lane</street>
<city>Anywhere</city>
<state>CA</state>
<zip>12345</zip>
</address>
</customer>
<item>
<description>Large Purple Dinosaur,
Generic</description>
<quantity>35</quantity>
<price>24.99</price>
</item>
<item>
<description>Catapult</description>
<quantity>1</quantity>
<price>149.95</price>
</item>
<item>
<description>300 foot measuring tape</description>
<quantity>1</quantity>
<price>9.95</price>
</item>
</order>
}
def calc_total(order)
total = 0
order.item.each do |item|
total += item.price.to_f * item.quantity.to_i
end
return total
end
puts "total: #{calc_total(doc.order)}"
======================================================
This is the file XML.rb that makes it work:
======================================================
require 'xmltreebuilder'
require 'perlvalue'
ELEMENT_NODE = 1; ATTRIBUTE_NODE = 2; TEXT_NODE = 3
module Kernel
def XML(xml)
RNX.new(xml)
end
end
class RNX
def initialize(xml)
@level = 0; @line = @source = ''; @path = []
builder = XML::DOM::Builder.new
begin
dom = builder.parse xml.gsub(/\s+/, '')
rescue
puts "#{$0}: #{$!} (in line #{builder.line}";
exit 1
end
dom.documentElement.normalize
traverse dom
eval %{
@#{dom.documentElement.nodeName} = PerlValue.new
#{@source}
def self.#{dom.documentElement.nodeName}
return @#{dom.documentElement.nodeName}
end
}
end
def traverse(node)
index = 0
node.childNodes.each do |n|
if n.nodeType == ELEMENT_NODE
@path[@level] = n.nodeName
if (n.nextSibling and
n.nextSibling.nodeName == n.nodeName) or (n.previousSibling and
n.previousSibling.nodeName == n.nodeName)
@path[@level] += "[#{index}]"
index += 1
end
@line = @path.join('.')
@level += 1; traverse n; @level -= 1
@path.delete_at(@level)
elsif n.nodeType == TEXT_NODE
@line += " = '#{n.data}'\n" if n.data
=~ /\S/ #optional!!!
@source += "@#{@line}"
end
end
end
end