1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
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("//return") 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 && 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] << 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+/, "") xml = xml.gsub(/(>)\s*(<)/, '\1\2') end # converts "true" and "false" to boolean values or # returns the given string otherwise. def self.string_to_bool?(string) return true if string == "true" return false if string == "false" string end end xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.some.namespace/"> <return> <authenticationValue> <orderToken>dfgdfg-4356345-sdfsdf-2345234-234</orderToken> <tokenHash>000111DDD888444777NNNZZZ555000</tokenHash> <client>dude</client> </authenticationValue> <success>true</success> </return> </ns2:authenticateResponse> </soap:Body> </soap:Envelope>' hash = Parser::soap_response_to_hash(xml)
Refactorings
No refactoring yet !
kotrin
August 8, 2009, August 08, 2009 05:49, permalink
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
require 'rubygems' require 'hpricot' class Parse def self.to_hash n h = {} n.each_child do |c| c.respond_to?(:children) ? h[c.name] = c.children.size>1 ? to_hash(c) : c.children[0].name : h[n.name] = c.name end h end end doc = Hpricot.XML xml.gsub(/(>)\s*(<)/, '\1\2').gsub(/\n+/, "") node = doc%'//return' hash = Parse.to_hash node
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.
[Update]
Now available as a gem at http://github.com/rubiii/apricoteatsgorilla