I have a string that I want to parse in Ruby:
string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
Is there an easy way to extract the data?
require 'json'
in your code.
This looks like JavaScript Object Notation (JSON). You can parse JSON that resides in some variable, e.g. json_string
, like so:
require 'json'
JSON.parse(json_string)
If you’re using an older Ruby, you may need to install the json gem.
There are also other implementations of JSON for Ruby that may fit some use-cases better:
YAJL C Bindings for Ruby
JSON::Stream
Just to extend the answers a bit with what to do with the parsed object:
# JSON Parsing example
require "rubygems" # don't need this if you're Ruby v1.9.3 or higher
require "json"
string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
parsed = JSON.parse(string) # returns a hash
p parsed["desc"]["someKey"]
p parsed["main_item"]["stats"]["a"]
# Read JSON from a file, iterate over objects
file = open("shops.json")
json = file.read
parsed = JSON.parse(json)
parsed["shop"].each do |shop|
p shop["id"]
end
'{ "a": "bob" }'
is valid. "{ 'a': 'bob' }"
is not.
JSON.parse
within a rescue block for JSON::ParserError
.
JSON.parse("[#{value}]")[0]
to avoid the error A JSON text must at least contain two octets!
As of Ruby v1.9.3 you don't need to install any Gems in order to parse JSON, simply use require 'json'
:
require 'json'
json = JSON.parse '{"foo":"bar", "ping":"pong"}'
puts json['foo'] # prints "bar"
See JSON at Ruby-Doc.
It looks like a JSON string. You can use one of many JSON libraries and it's as simple as doing:
JSON.parse(string)
This is a bit late but I ran into something interesting that seems important to contribute.
I accidentally wrote this code, and it seems to work:
require 'yaml'
CONFIG_FILE = ENV['CONFIG_FILE'] # path to a JSON config file
configs = YAML.load_file("#{CONFIG_FILE}")
puts configs['desc']['someKey']
I was surprised to see it works since I am using the YAML library, but it works.
The reason why it is important is that yaml
comes built-in with Ruby so there's no gem install.
I am using versions 1.8.x and 1.9.x - so the json
library is not built in, but it is in version 2.x.
So technically - this is the easiest way to extract the data in version lower than 2.0.
Don't see any answers here that mention parsing directly to an object other than a Hash, but it is possible using the poorly-documented object_class option(see https://ruby-doc.org/stdlib-2.7.1/libdoc/json/rdoc/JSON.html):
JSON.parse('{"foo":{"bar": 2}}', object_class: OpenStruct).foo.bar
=> 2
The better way to read that option is "The ruby class that a json object turns into", which explains why it defaults to Hash. Likewise, there is an array_class option for json arrays.
I suggest Oj as it is waaaaaay faster than the standard JSON library.
(see performance comparisons here)
If you want to deserialise to your own class instead of OpenStruct it doesn't take a lot of work to make the following possible:
require 'json'
# result is an instance of MyClass
result = JSON.parse(some_json_string, object_class: MyClass)
All you have to do is to provide a zero-argument constructor and implement the #[]=
method which JSON.parse
will call. If you don't want to expose it, it's sufficient to let it be private:
class MyClass
attr_reader :a, :b
private
def []=(key, value)
case key
when 'a' then @a = value
when 'b' then @b = value
end
end
end
Trying it out in irb:
> JSON.parse('{"a":1, "b":2}', object_class: MyClass)
=> #<MyClass:0x00007fe00913ae98 @a=1, @b=2>
A caveat with this approach is that it only works for flat structures, because the object_class
argument really tells the parser what class it should use to deserialise JSON objects in the string instead of Hash
(see the similar argument array_class
for the analogous operation for JSON arrays). For nested structures this will mean you will use the same class to represent all layers:
> JSON.parse('{"a":1, "b":{ "a": 32 }}', object_class: MyClass)
=> #<MyClass:0x00007fb5110b2b38 @a=1, @b=#<MyClass:0x00007fb5110b2908 @a=32>>
Success story sharing
JSON.parse(string, symbolize_names: true) #=> {key: :value}
require 'json'
in your code.