Xml Processing in IronRuby - Help me improve the wrapper


#1

Hello,

I’ve just been playing around with IronRuby and processing some XML
from a REST service. I’ve created this wrapper:
require ‘mscorlib’
require ‘System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089’
include System
require ‘System.Xml, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089’
include System::Xml

#Wrapper around .Net XMLDocument to make it easier to query with -
more like REXML
class Document
def initialize(xml)
@document = XmlDocument.new
@document.load_xml xml
end

def get(xpath) #.collect - I want to pass the block in, not have
to use it on the outside.
@document.SelectNodes(xpath)
end
end

class XmlNodeList
def get(name)
list.select {|e| e.Name == name}
end
end

class XmlElement
def get(name)
if has_attribute name
get_attribute name
else
get_elements_by_tag_name name
end
end

def text
inner_text
end
end

My first problem is that I’m getting XmlElement object instead of
XmlNode from an XmlNodeList. If I had XmlNode, then I could do
e[‘n’].text - which is what I want. As a result, my code to access
the N element looks like this:

require ‘xml’
@document = Document.new(‘testaaa’)
@document.get(‘x’).collect {|n| puts n.text}

testaaa

@document.get(‘x/x1’).collect {|e| e.get(‘n’).collect {|n| puts n.text}}
test

Why am I getting a XmlElement object? How can I access my xml in a
cleaner fashion?

My second problem, I wanted to be able to pass in a block

def get(xpath)
@document.SelectNodes(xpath).collect {yield}
end

In this case, I get the following error:

@document.get(‘x’) {|n| puts n.text}
:1: undefined method text' for nil:NilClass (NoMethodError) from xml.rb:27:inget’

I changed it to {|n| yield}, but I get the same error. The object is
always nil. What am I doing wrong?

Hope this all makes sense.

Thanks

Ben


#2

I wrote something similar a while ago

http://github.com/casualjim/ironnails/tree/master/IronNails/vendor/iron_nails/lib/iron_xml.rb

statuses = []
IronXml.parse(‘friends_timeline.xml’, :document) do |doc|
doc.elements(‘statuses’) do |status|
st = Status.new
st.id = status.element(‘id’).value
st.created_at = status.element(‘created_at’).value
st.text = status.element(‘text’).value
st.source = status.element(‘source’).value
st.truncated = status.element(‘truncated’).value
st.in_reply_to_status_id =
status.element(‘in_reply_to_status_id’).value
st.in_reply_to_user_id = status.element(‘in_reply_to_user_id’).value
st.favorited = status.element(‘favorited’).value
status.elements(‘user’) do |user|
# process user here
end

statuses << st

end
end

or

statuses = []
IronXml.parse(‘friends_timeline.xml’, :document) do |doc|
doc.statuses do |status|
st = Status.new
st.id = status.id
st.created_at = status.created_at
st.text = status.text
st.source = status.source
st.truncated = status.truncated
st.in_reply_to_status_id = status.in_reply_to_status_id
st.in_reply_to_user_id = status.in_reply_to_user_id
st.favorited = status.favorited
status.user do |user|
# process user here
end

statuses << st

end
end

You can maybe look there for inspiration :slight_smile:


#3

Excellent!! Thank you. I was missing select_single_node and &b :wink:

Wrapper is now sorted, and I can do:
require ‘xml’
@document =
Document.new(‘testabctestertesteraaa’)

@document.elements(‘x/x1’) do |e|
puts e.get(‘z’)
e.node(‘y’) {|y| puts y.get(‘q’)}
end

Thanks
Ben