<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:www.refactormycode.com,2007:users1562</id>
  <link type="application/atom+xml" href="http://www.refactormycode.com/users/1562" rel="self"/>
  <title>smacks</title>
  <updated>Thu Jun 25 12:23:20 -0700 2009</updated>
  <entry>
    <id>tag:www.refactormycode.com,2007:Code932</id>
    <published>2009-06-25T12:23:20-07:00</published>
    <updated>2009-11-07T14:16:32-08:00</updated>
    <title>[Ruby] Create a Hash from XML using Hpricot</title>
    <content type="html">&lt;p&gt;With ruby you could use REXML's Hash.from_xml, but I really don't like that. The reason for me to write this, was to convert various xml responses from different soap services to a hash. I don't want any namespaces, attributes, etc. The code includes a (pretty simple) example xml response. Other responses might include multiple nodes with the same name, as well as empty-element tags. This piece of code was heavily inspired by cobravsmongoose, but it doesn't follow the BadgerFish conventions and uses Hpricot instead of REXML.&lt;/p&gt;

&lt;p&gt;[Update]
&lt;br /&gt;Now available as a gem at &lt;a href="http://github.com/rubiii/apricoteatsgorilla" target="_blank"&gt;http://github.com/rubiii/apricoteatsgorilla&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;require 'hpricot'

class Parser

  # converts a given soap response xml into a hash.
  def self.soap_response_to_hash(xml)
    xml = prepare_xml(xml)
    doc = Hpricot.XML(xml)
    root = doc.at(&amp;quot;//return&amp;quot;)
    xml_node_to_hash(root)
  end

private

  # converts xml nodes into a hash. recursively calls
  # itself in case of nested xml nodes.
  def self.xml_node_to_hash(node)
    this_node = {}

    node.each_child do |child|
      if child.children.nil?
        key, value = child.name, nil
      elsif child.children.size == 1 &amp;amp;&amp;amp; child.children.first.text?
        key, value = child.name, string_to_bool?(child.children.first.raw_string)
      else
        key, value = child.name, xml_node_to_hash(child)
      end

      current = this_node[key]
      case current
        when Array:
          this_node[key] &amp;lt;&amp;lt; value
        when nil
          this_node[key] = value
        else
          this_node[key] = [current.dup, value]
      end
    end

    this_node
  end

  # removes line breaks and whitespace between xml nodes.
  def self.prepare_xml(xml)
    xml = xml.gsub(/\n+/, &amp;quot;&amp;quot;)
    xml = xml.gsub(/(&amp;gt;)\s*(&amp;lt;)/, '\1\2')
  end

  # converts &amp;quot;true&amp;quot; and &amp;quot;false&amp;quot; to boolean values or
  # returns the given string otherwise.
  def self.string_to_bool?(string)
    return true if string == &amp;quot;true&amp;quot;
    return false if string == &amp;quot;false&amp;quot;
    string
  end

end

xml = '&amp;lt;soap:Envelope xmlns:soap=&amp;quot;http://schemas.xmlsoap.org/soap/envelope/&amp;quot;&amp;gt;
   &amp;lt;soap:Body&amp;gt;
      &amp;lt;ns2:authenticateResponse xmlns:ns2=&amp;quot;http://v1_0.ws.some.namespace/&amp;quot;&amp;gt;
         &amp;lt;return&amp;gt;
            &amp;lt;authenticationValue&amp;gt;
               &amp;lt;orderToken&amp;gt;dfgdfg-4356345-sdfsdf-2345234-234&amp;lt;/orderToken&amp;gt;
               &amp;lt;tokenHash&amp;gt;000111DDD888444777NNNZZZ555000&amp;lt;/tokenHash&amp;gt;
               &amp;lt;client&amp;gt;dude&amp;lt;/client&amp;gt;
            &amp;lt;/authenticationValue&amp;gt;
            &amp;lt;success&amp;gt;true&amp;lt;/success&amp;gt;
         &amp;lt;/return&amp;gt;
      &amp;lt;/ns2:authenticateResponse&amp;gt;
   &amp;lt;/soap:Body&amp;gt;
&amp;lt;/soap:Envelope&amp;gt;'

hash = Parser::soap_response_to_hash(xml)&lt;/pre&gt;</content>
    <author>
      <name>smacks</name>
      <email>me@d-harrington.com</email>
    </author>
    <link type="text/html" href="http://www.refactormycode.com/codes/932-create-a-hash-from-xml-using-hpricot" rel="alternate"/>
  </entry>
</feed>

