19

I using some service that returns xml:

response = HTTParty.post(service_url)
response.parsed_response 
=> "\n\t<Result>\n<success>\ntrue\n</success>\n</Result>"

I need to convert this string to hash. Something like this:

response.parsed_response.to_hash
=> {:result => { :success => true } }

Which way to do this?

sites
  • 21,417
  • 17
  • 87
  • 146
Rodrigo
  • 5,435
  • 5
  • 42
  • 78

5 Answers5

42

The built-in from_xml Rails Hash method will do precisely what you want. In order to get your response.parsed_response correctly mapped to a hash, you'll need to gsub() out the newlines:

hash = Hash.from_xml(response.parsed_response.gsub("\n", "")) 
hash #=> {"Result"=>{"success"=>"true"}}

In the context of parsing a hash in Rails, objects of String type are not substantively different than those of Symbol from a general programming perspective. However, you can apply the Rails symbolize_keys method to the output:

symbolized_hash = hash.symbolize_keys
#=> {:Result=>{"success"=>"true"}} 

As you can see, symbolize_keys doesn't operate on any nested hashes, but you could potentially iterate through inner hashes and apply symbolize_keys.

The final piece of the puzzle is to convert the string "true" to the boolean true. AFAIK, there's no way to do this on your hash in place, but if you're iterating/operating on it, you could potentially implement a solution like the one suggested in this post:

def to_boolean(str)
     return true if str == "true"
     return false if str == "false"
     return nil
end

Basically, when you reach the inner key-value pair, you'd apply to_boolean() to the value, which is currently set to "true". In your example, the return value is the boolean true.

Community
  • 1
  • 1
zeantsoi
  • 25,857
  • 7
  • 69
  • 61
  • Just FYI, Rails has a [deep_symbolize_keys](https://api.rubyonrails.org/classes/Hash.html#method-i-deep_symbolize_keys) method, so you don't need to iterate through inner hashes – electr0sheep Sep 21 '21 at 20:00
12

Use nokogiri to parse XML response to ruby hash. It's pretty fast.

require 'active_support/core_ext/hash'  #from_xml 
require 'nokogiri'

doc = Nokogiri::XML(response_body)
Hash.from_xml(doc.to_s)
SaiyanGirl
  • 16,376
  • 11
  • 41
  • 57
PythonDev
  • 4,287
  • 6
  • 32
  • 39
5

You can try this below:

require 'active_support/core_ext/hash/conversions'  
str = "\n\t<Result>\n<success>\ntrue\n</success>\n</Result>".gsub("\n", "").downcase

Hash.from_xml(str)
# => {"result"=>{"success"=>"true"}}
Abdullah
  • 2,015
  • 2
  • 20
  • 29
Rajarshi Das
  • 11,778
  • 6
  • 46
  • 74
3

Use gem Nokogir

doc = Nokogiri::XML(xml_string)

data = doc.xpath("//Result").map do |result|
  [
    result.at("success").content
  ]
end

These tutorials may help you.

rony36
  • 3,277
  • 1
  • 30
  • 42
1

I found a gem which does exactly that:

gem 'actionpack-xml_parser'

Basically, each node represents a key. With the following XML:

<person><name>David</name></person>

The resulting parameters will be:

{"person" => {"name" => "David"}}

https://github.com/eileencodes/actionpack-xml_parser

Jan
  • 12,992
  • 9
  • 53
  • 89