XML Data Binding for Ruby ?

M

Markus Jais

hello

I am currently preparing an article about Ruby and XML where I will
show some XML modules for ruby. only a short example to show
people what's possible and that Ruby is a great alternative to Java/C#
when processing XML.

I am searching for a module where I can construct a Ruby Class from
an arbitrary XML document, similar to Python's anobind

with this I can take an XML document like this

<?xml version="1.0"?>
<birds>
<raptor wingspan='240cm'>
<english_name>White-tailed Eagle</english_name>
<german_name>Seeadler</german_name>
</raptor>
<raptor wingspan='80cm'>
<english_name>Kestrel</english_name>
<german_name>Turmfalke</german_name>
</raptor>
</birds>


and this python code
=============
#!/usr/bin/env python

import anobind
from Ft.Xml import InputSource
from Ft.Lib import Uri

file_uri = Uri.OsPathToUri('raptors.xml', attemptAbsolute=1)
input_source = InputSource.DefaultFactory.fromUri(file_uri)

binding = anobind.binder().read_xml(input_source)

print binding.birds.raptor[0].german_name.text_content()
print

for raptor in binding.birds.raptor:
print raptor.wingspan
print raptor.german_name.text_content()
print raptor.english_name.text_content()
print
============

is there something similar for ruby ?


thanks in advance


regards

Markus
 
S

Sean Russell

Markus Jais said:
with this I can take an XML document like this ....
and this python code
=============
#!/usr/bin/env python

import anobind

Here's something that'll do the job. I just threw this together, so
it is probably fragile, and it is one-way -- it only converts XML to
Ruby objects. But it implements the functionality that you described
in the example you gave -- it does so in 25 lines of code, and took
about as many minutes to write and test. :) I'm sure it could be
compressed and/or optimized; there were a number of solutions to
choose from.

By the way, I personally believe that this class of XML-to-Objects
mechanism has extremely limited use. If you use real XML objects and
XPath, you have access to a much broader set of XML documents, and it
is often easier to work with -- especially with deeply nested XML.
And if all you're looking for is serialization, there are a number of
projects available; some, even, that generate XML -- albeit, not as
clear of a 1-to-1 mapping as here.

For an example of where I expect the Python code to fail (and I *know*
the Ruby code I've supplied will fail), given:

<a b="B1">
<b>B2</b>
<some-tag>TAG1</some-tag>
<another.tag>TAG2</another.tag>
<:tag>TAG3</:tag>
</a>

How do you differentiate between @b and b? Can you get any of the TAG
text values?

Anyway, here's the Ruby version of the Python module.

############## START CODE
# A quasi-serialization class. Accepts a limited subset of XML and
turns it
# into Ruby objects.
require "rexml/document"

class XMLObject
def initialize element
@element = element
@methods = {}
end
def method_missing method
m_name = method.to_s
return @methods[m_name] if @methods[m_name]
el = REXML::XPath.match( @element, m_name )
case el.size
when 0
return @element.attributes[ m_name ]
when 1
@methods[ m_name ] = XMLObject.new( el[0] )
else
el.collect!{ |e| XMLObject.new( e ) }
@methods[ m_name ] = el
end
return @methods[m_name]
end
def to_s
return REXML::XPath.match( @element, "text()" ).join.squeeze("
\n\t").strip
end
end

# Cut here if you want to reuse this code. Everything that follows is
# just an example of how it works.

d = REXML::Document.new( DATA.read )
a = XMLObject.new( d.root )
puts "a.b.x = #{a.b.x}"
puts "a.b.to_s = #{a.b.to_s}"
puts "a.b.c[0].y = #{a.b.c[0].y}"
puts "a.b.c[1].to_s = #{a.b.c[1].to_s}"

__END__
<a>
<b x="1">
<c y="2">C1</c>
<c>C2</c>
Text
</b>
</a>



--- SER
 
D

darkelf3

thanks a lot.

markus

Sean said:
Markus Jais said:
with this I can take an XML document like this ...
and this python code
=============
#!/usr/bin/env python

import anobind

Here's something that'll do the job. I just threw this together, so
it is probably fragile, and it is one-way -- it only converts XML to
Ruby objects. But it implements the functionality that you described
in the example you gave -- it does so in 25 lines of code, and took
about as many minutes to write and test. :) I'm sure it could be
compressed and/or optimized; there were a number of solutions to
choose from.

By the way, I personally believe that this class of XML-to-Objects
mechanism has extremely limited use. If you use real XML objects and
XPath, you have access to a much broader set of XML documents, and it
is often easier to work with -- especially with deeply nested XML.
And if all you're looking for is serialization, there are a number of
projects available; some, even, that generate XML -- albeit, not as
clear of a 1-to-1 mapping as here.

For an example of where I expect the Python code to fail (and I *know*
the Ruby code I've supplied will fail), given:

<a b="B1">
<b>B2</b>
<some-tag>TAG1</some-tag>
<another.tag>TAG2</another.tag>
<:tag>TAG3</:tag>
</a>

How do you differentiate between @b and b? Can you get any of the TAG
text values?

Anyway, here's the Ruby version of the Python module.

############## START CODE
# A quasi-serialization class. Accepts a limited subset of XML and
turns it
# into Ruby objects.
require "rexml/document"

class XMLObject
def initialize element
@element = element
@methods = {}
end
def method_missing method
m_name = method.to_s
return @methods[m_name] if @methods[m_name]
el = REXML::XPath.match( @element, m_name )
case el.size
when 0
return @element.attributes[ m_name ]
when 1
@methods[ m_name ] = XMLObject.new( el[0] )
else
el.collect!{ |e| XMLObject.new( e ) }
@methods[ m_name ] = el
end
return @methods[m_name]
end
def to_s
return REXML::XPath.match( @element, "text()" ).join.squeeze("
\n\t").strip
end
end

# Cut here if you want to reuse this code. Everything that follows is
# just an example of how it works.

d = REXML::Document.new( DATA.read )
a = XMLObject.new( d.root )
puts "a.b.x = #{a.b.x}"
puts "a.b.to_s = #{a.b.to_s}"
puts "a.b.c[0].y = #{a.b.c[0].y}"
puts "a.b.c[1].to_s = #{a.b.c[1].to_s}"

__END__
<a>
<b x="1">
<c y="2">C1</c>
<c>C2</c>
Text
</b>
</a>



--- SER
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top