26

Possible Duplicate:
Parsing a JSON string in ruby

Is it possible to convert a JSON string into a Ruby object? I would like to access its information with an expression similar to:

drawer.stations.tv.header

JSON string:

{
  "drawer" : {
    "stations" : {
      "tv" : {
        "header" : "TV Channels",
        "logos" : {
          "one" : "www1",
          "two" : "www2",
          "three" : "www3"
        }
      }
    }
  }
}
Community
  • 1
  • 1
mickael
  • 2,033
  • 7
  • 25
  • 38
  • Hiya, kind of... but not quite, ie, that one is just parsing the JSON but not converting the has into an object. So depending on what your needs are you could be fine with that explanation or use the explanation here, which is different... – mickael Dec 17 '12 at 19:34
  • 2
    I do not believe this is a duplicate, due to his request to access the keys as methods. For this, the recommend the answer from Sergio Tulentsev. – Matt Scilipoti Jul 10 '15 at 20:20

3 Answers3

56

You can parse the string into a ruby hash and then turn it into a Mash. Mash provides you with method-like access.

require 'json'
require 'hashie'

hash = JSON.parse json_string
obj = Hashie::Mash.new hash
obj.drawer.stations.tv.header # => "TV Channels"

Update

You can also do it without a 3rd party gem, using ruby's own OpenStruct:

require 'ostruct'
require 'json'

obj = JSON.parse(json_string, object_class: OpenStruct)
obj.drawer.stations.tv.header # => "TV Channels"
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Great the Hashie. Mutch better than method_missing. Thanks for the tip. – BernardK Dec 16 '12 at 10:00
  • Hey Sergio, gracias!!!! Thanks for taking the time to reply even though I accepted a previous answer as valid. That one was not bad, as a workaround. But your explanation is exactly what I was looking for... many thanks!!!! – mickael Dec 16 '12 at 21:48
  • Some API's may return a json array of hashes. In that case use something like `@users = JSON.parse(resource.get).map { |hash| Hashie::Mash.new(hash) }` – Hal50000 Mar 09 '16 at 16:30
  • 1
    @Hal50000: check out the update :) – Sergio Tulentsev Mar 09 '16 at 16:32
  • @SergioTulentsev nice! I just changed my code to use `OpenStruct` since it will handle both cases without manually wrangling the hashes – Hal50000 Mar 09 '16 at 16:44
  • @Hal50000 You can do the same with Hashie::Mash. This is also valid: `obj = JSON.parse(json_string, object_class: Hashie::Mash)` – Adrian May 16 '17 at 03:40
  • @adrian: why someone ever do that? With `Hashie::Mash` there's a much simpler construct. Which is shown in the first part of the answer. – Sergio Tulentsev May 16 '17 at 06:24
  • @SergioTulentsev I was just telling @Hal50000 that you can do the same with `Hashie::Mash`, because the reason he/she chose to use `OpenStruct` is that he/she seemed to think you can't do the same with `Hashie::Mash` and `Hashie::Mash` needs an intermediate hash. – Adrian May 17 '17 at 07:17
  • @adrian: ah, I see! – Sergio Tulentsev May 17 '17 at 07:21
  • It has some quirks but checkout [class2](https://github.com/sshaw/class2/). – sshaw Nov 22 '21 at 02:18
9

if your parse this string to ruby object, it will return a ruby Hash object, you can get it like this

  ruby_obj = JSON.parse(json_string)
  ruby_obj['drawer']['stations']['tv']['header']
Richie Min
  • 654
  • 5
  • 15
3
require 'json'

json_info = %q(
{
  "drawer" : {
    "stations" : {
      "tv" : {
        "header" : "TV Channels",
        "logos" : {
          "one" : "www1",
          "two" : "www2",
          "three" : "www3"
        }
      }
    }
  }
}
)

class MyJson
    def self.for(p_jason_string)
        self.new(JSON.parse(p_jason_string))
    end

    def initialize(p_info)
        @info = p_info
    end

    def inspect
        @info.inspect
    end

    def method_missing(p_missing_method_name)
        print 'mm '; p p_missing_method_name
        key = p_missing_method_name.to_s

        if @info.has_key?(key)
        then
            MyJson.new(@info[key])
        else
            puts "no key #{p_missing_method_name}"
        end
    end
end # class MyJson

holder = MyJson.for(json_info)
puts '-----holder.drawer'
p holder.drawer
puts '-----holder.drawer.stations'
p holder.drawer.stations
puts '-----holder.drawer.stations.tv.header'
p holder.drawer.stations.tv.header

Execution :

$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ ruby -w t.rb
-----holder.drawer
mm :drawer
{"stations"=>{"tv"=>{"header"=>"TV Channels", "logos"=>{"one"=>"www1", "two"=>"www2", "three"=>"www3"}}}}
-----holder.drawer.stations
mm :drawer
mm :stations
{"tv"=>{"header"=>"TV Channels", "logos"=>{"one"=>"www1", "two"=>"www2", "three"=>"www3"}}}
-----holder.drawer.stations.tv.header
mm :drawer
mm :stations
mm :tv
mm :header
"TV Channels"

Note that I use RVM and have done nothing special to have json working, must have been automatically installed.

BernardK
  • 3,674
  • 2
  • 15
  • 10