HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyrailsMinor

Parsing data from XML using Ruby and Nokogiri

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
xmlparsingusingrubyandfromdatanokogiri

Problem

I have a method that parses XML into an array of hashes.

Here is the original XML:







Here is the final array of hashes:

[{:queuePosition=>"0", :typeID=>"20495", :level=>"3", :startSP=>"2829", :endSP=>"16000", :startTime=>"2013-03-04 16:25:50", :endTime=>"2013-03-05 01:02:20"}, {:queuePosition=>"1", :typeID=>"19767", :level=>"4", :startSP=>"40000", :endSP=>"226275", :startTime=>"2013-03-05 01:02:20", :endTime=>"2013-03-07 06:40:31"}]


Here is the method, which uses Nokogiri for parsing:

def get_training_queue(xml_data)
queue = []
xml_data.xpath("//row").each do |skill_in_queue|
skill = {}
skill_in_queue.attributes.each do |details|
skill[details[0].to_s.to_sym] = details[1].to_s
end
queue

This works great, but it feels a bit inelegant.

  • I'm curious if there is a better way to create the hashes within the internal loop?



  • I am calling to_s` because I couldn't figure out a way to pull out just the key/value without doing so, and it 'feels' like I'm missing some more elegant way of doing that.

Solution

Notes:

  • Use functional Enumerable#map instead of imperative pattern obj = [] + Enumerable#each + Array#



  • Use Kernel#Hash to build a hash from its pairs. Note that this method is very ugly and some prefer a more OOP approach, in that case check Enumerable#mash from Facets.



  • Use nokogiri_node.text.



  • Arguments in blocks can be unpacked.



I'd write:

require 'nokogiri'
require 'facets'

def get_training_queue(xml_data)
  xml_data.xpath("//row").map do |skill_in_queue|
    skill_in_queue.attributes.mash do |name, attribute|
      [name.to_sym, attribute.text]
    end
  end
end


All those
each`s and in-place updates show that you think in imperative terms instead of functional, check this wiki page.

Code Snippets

require 'nokogiri'
require 'facets'

def get_training_queue(xml_data)
  xml_data.xpath("//row").map do |skill_in_queue|
    skill_in_queue.attributes.mash do |name, attribute|
      [name.to_sym, attribute.text]
    end
  end
end

Context

StackExchange Code Review Q#23449, answer score: 4

Revisions (0)

No revisions yet.