title: Which Ruby XML library would you recommend for a 2.4MB XML file?
body: <p>I have a 2.4 MB XML file, an export from Microsoft Project (hey I'm the victim here!) from which I am requested to extract certain details for re-presentation. Ignoring the intelligence or otherwise of the request, which library should I try first from a Ruby perspective?</p>
  
  <p>I'm aware of the following (in no particular order):</p>
  
  <ul>
  <li><a href="http://www.germane-software.com/software/rexml/" rel="noreferrer">REXML</a></li>
  <li><a href="http://www.chilkatsoft.com/ruby-xml.asp" rel="noreferrer">Chilkat Ruby XML library</a></li>
  <li><a href="http://code.whytheluckystiff.net/hpricot/wiki/HpricotXML" rel="noreferrer">hpricot XML</a></li>
  <li><a href="http://libxml.rubyforge.org/" rel="noreferrer">libXML</a></li>
  </ul>
  
  <p>I'd prefer something packaged as a Ruby gem, which I suspect the Chilkat library is not.</p>
  
  <p>Performance isn't a major issue - I don't expect the thing to need to run more than once a day (once a week is more likely). I'm more interested in something that's as easy to use as anything XML-related is able to get.</p>
  
  <p>EDIT: I tried the gemified ones:</p>
  
  <p>hpricot is, by a country mile, easiest. For example, to extract the content of the SaveVersion tag in this XML (saved in a file called, say 'test.xml')</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
  &lt;Project xmlns="http://schemas.microsoft.com/project"&gt;
      &lt;SaveVersion&gt;12&lt;/SaveVersion&gt;
  &lt;/Project&gt;
  </code></pre>
  
  <p>takes something like this: </p>
  
  <pre><code>doc = Hpricot.XML(open('test.xml'))
  version = (doc/:Project/:SaveVersion).first.inner_html
  </code></pre>
  
  <p>hpricot seems to be relatively unconcerned with namespaces, which in this example is fine: there's only one, but would potentially be a problem with a complex document. Since hpricot is also very slow, I rather imagine this would be a problem that solves itself.</p>
  
  <p>libxml-ruby is an order of magnitude faster, understands namespaces (it took me a good couple of hours to figure this out) and is altogether much closer to the XML metal - XPath queries and all the other stuff are in there. This is not necessarily a Good Thing if, like me, you open up an XML document only under conditions of extreme duress. The helper module was mostly helpful in providing examples of how to handle a default namespace effectively. This is roughly what I ended up with (I'm not in any way asserting its beauty, correctness or other value, it's just where I am right now):</p>
  
  <pre><code>xml_parser = XML::Parser.new
  xml_parser.string = File.read(path)
  doc = xml_parser.parse
  @root = doc.root
  @scopes = { :in_node =&gt; '', :in_root =&gt; '/', :in_doc =&gt; '//' }
  @ns_prefix = 'p'
  @ns = "#{@ns_prefix}:#{@root.namespace[0].href}"
  version = @root.find_first(xpath_qry("Project/SaveVersion", :in_root), @ns).content.to_i
  
  def xpath_qry(tags, scope = :in_node)
    "#{@scopes[scope]}" + tags.split(/\//).collect{ |tag| "#{@ns_prefix}:#{tag}"}.join('/')
  end
  </code></pre>
  
  <p>I'm still debating the pros and cons: libxml for its extra rigour, hpricot for the sheer style of _why's code.</p>
  
  <p>EDIT again, somewhat later: I discovered HappyMapper ('gem install happymapper') which is hugely promising, if still at an early stage. It's declarative and mostly works, although I have spotted a couple of edge cases that I don't have fixes for yet. It lets you do stuff like this, which parses my Google Reader OPML:</p>
  
  <pre><code>module OPML
    class Outline
      include HappyMapper
      tag 'outline'
      attribute :title, String
      attribute :text, String
      attribute :type, String
      attribute :xmlUrl, String
      attribute :htmlUrl, String
      has_many :outlines, Outline
    end
  end
  
  xml_string = File.read("google-reader-subscriptions.xml")
  
  sections = OPML::Outline.parse(xml_string)
  </code></pre>
  
  <p>I already love it, even though it's not perfect yet.</p>