I'm writing some REST-ian code to talk to the Amazon Web Services
since there are some bugs in SOAP4R (which I'll explain in a future
mail).
While manually parsing the XML that is returned from Amazon's REST
interface, I wound up writing the following code:
item.details = []
doc.elements.each('ItemLookupResponse/Items/Item') do |item|
# construct an item_detail
item_details << item_detail
end
This doesn't *feel* right. I wanted to use collect() but it's not
implemented in REXML. Is there a more Ruby-esque approach to gathering
some objects into an array while parsing XML?
I'm writing some REST-ian code to talk to the Amazon Web Services
since there are some bugs in SOAP4R (which I'll explain in a future
mail).
While manually parsing the XML that is returned from Amazon's REST
interface, I wound up writing the following code:
item.details =
doc.elements.each('ItemLookupResponse/Items/Item') do |item|
# construct an item_detail
item_details << item_detail
end
This doesn't *feel* right. I wanted to use collect() but it's not
implemented in REXML. Is there a more Ruby-esque approach to gathering
some objects into an array while parsing XML?
You seem to have left a detail out of your code (where does 'item_detail' come from?) but that's generally what I do in similar circumstances.
Alternatively, you could write your own #collect method:
class REXML::Elements
def collect( xpath=nil )
vals =
self.each(xpath){ |el| vals << yield el }
vals
end
end
Or, perhaps you could modify the existing each method to return an array with no block supplied:
class REXML::Elements
alias_method :__each :each
def each( xpath=nil, &block )
if block_given?
__each( xpath, &block )
else
els =
__each( xpath ){ |el| els << el }
els
end
end
end
Then you could simply:
item_details = doc.elements.each( '...' ).collect{ ... }
But then this is really exactly what Xpath.match does:
item_details = XPath.match( doc, 'ItemLookupResponse/Items/Item' ).collect{ ... }
(Aside: What possessed the author of the REXML library to put all the XPath stuff in its own class with class methods? Why not simply "doc.match( my_xpath )"? Whenever I see a class method that takes a single object for an argument to set scope, it screams to me that it should be a method on that object.)
Hope that helps,
···
On May 19, 2005, at 8:15 AM, John Lam wrote:
item.details =
doc.elements.each('ItemLookupResponse/Items/Item') do |item|
# construct an item_detail
item_details << item_detail
end
This doesn't *feel* right. I wanted to use collect() but it's not
implemented in REXML. Is there a more Ruby-esque approach to gathering
some objects into an array while parsing XML?
Aside: What possessed the author of the REXML library to put all the
XPath stuff in its own class with class methods? Why not simply
Three things. First, XPath is not part of the XML spec -- it is its
own spec, and has nothing to do with the tree (DOM) parser aside from
the association an implementor makes. Second, because *someday*,
you'll be able to use XPaths on things other than the DOM parser.
But the primary reason is historical. There isn't really any reason
why there couldn't be an Element#match function that delegates to an
XPath instance. Suggestions for improvements are always welcome,
although I warn you that I'm not prone to trolling the newsgroups for
them.