Question
I have a JSON-formatted string in Ruby:
string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
What is the simplest way to parse this string and extract values from it, including nested data?
Short Answer
By the end of this page, you will understand how to parse a JSON string in Ruby using the built-in JSON library, how to access nested hash values, and how to avoid common errors when working with parsed JSON data.
Concept
JSON is a text format used to represent structured data. In Ruby, when you parse a JSON string, Ruby converts that text into normal Ruby objects such as Hash, Array, String, Integer, Float, true, false, and nil.
For JSON objects like this:
{"name":"Alice","age":30}
Ruby usually converts them into a Hash:
{"name" => "Alice", "age" => 30}
This matters because many real programs receive data as JSON:
- API responses
- Configuration files
- Webhook payloads
- Data exchanged between services
- Frontend-to-backend communication
If you can parse JSON, you can turn raw text into data your Ruby code can actually use.
In your example, the JSON contains nested objects. After parsing, you can access them with chained hash lookups.
Ruby does not automatically treat a JSON string as a hash. You must first parse it with the json library.
Mental Model
Think of a JSON string as a sealed cardboard box with labels written on the outside. You can see it is structured, but you cannot directly grab the contents.
Parsing is the act of opening the box and organizing everything onto shelves.
- The JSON string is the sealed box
JSON.parseopens the box- Ruby hashes and arrays are the organized shelves
- Keys like
"desc"or"stats"are labels you use to find items
Before parsing, it is just text. After parsing, it becomes usable Ruby data.
Syntax and Examples
The usual way to parse JSON in Ruby is:
require 'json'
data = JSON.parse(json_string)
Basic example
require 'json'
string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
data = JSON.parse(string)
puts data["desc"]["someKey"] # someValue
puts data["desc"]["anotherKey"] # value
puts data["main_item"]["stats"]["a"] # 8
puts data["main_item"]["stats"]["b"] # 12
puts data["main_item"]["stats"]["c"] # 10
What the parsed result looks like
{
"desc" => {
"someKey" => "someValue",
"anotherKey" => "value"
},
"main_item" => {
"stats" => {
"a" => 8,
"b" => 12,
"c" => 10
}
}
}
Using symbol keys
By default, JSON.parse creates string keys. If you want symbols instead:
require 'json'
string = '{"desc":{"someKey":"someValue"}}'
data = JSON.parse(string, symbolize_names: true)
puts data[:desc][:someKey]
Use this only when you intentionally want symbol keys, and be consistent throughout your code.
Step by Step Execution
Consider this example:
require 'json'
string = '{"main_item":{"stats":{"a":8}}}'
data = JSON.parse(string)
value = data["main_item"]["stats"]["a"]
puts value
Step by step
- Ruby loads the JSON library with:
require 'json'
- The variable
stringcontains plain text:
'{"main_item":{"stats":{"a":8}}}'
At this point, Ruby sees it as a String, not a Hash.
JSON.parse(string)reads the JSON text and converts it into nested Ruby hashes:
{
"main_item" => {
"stats" => {
"a" => 8
}
}
}
data["main_item"]returns:
{ "stats" => { "a" => 8 } }
data["main_item"]["stats"]returns:
{ "a" => 8 }
data["main_item"]["stats"]["a"]returns:
8
puts valueprints:
8
Real World Use Cases
Parsing JSON in Ruby is common in many practical situations:
- Calling APIs: A Ruby app fetches weather, payment, or user data from an external service.
- Rails controllers: Incoming request bodies may be JSON.
- Background jobs: Workers process JSON payloads from queues.
- Configuration: Some tools store settings in JSON files.
- Logging and analytics: Events may be stored or transmitted as JSON.
- Web scraping or integrations: External systems often return JSON instead of XML.
Example: parsing an API response
require 'json'
response_body = '{"user":{"name":"Ava","active":true}}'
user_data = JSON.parse(response_body)
puts user_data["user"]["name"] # Ava
puts user_data["user"]["active"] # true
Real Codebase Usage
In real projects, developers rarely stop at just JSON.parse. They usually combine parsing with validation and safe access.
1. Parse once, then reuse
data = JSON.parse(response.body)
user_name = data["user"]["name"]
Avoid parsing the same JSON string repeatedly.
2. Guard against missing keys
if data["main_item"] && data["main_item"]["stats"]
puts data["main_item"]["stats"]["a"]
end
Or with dig:
puts data.dig("main_item", "stats", "a")
3. Handle invalid JSON
require 'json'
begin
data = JSON.parse(string)
rescue JSON::ParserError => e
puts "Invalid JSON: #{e.message}"
end
4. Normalize key style
Teams often choose either:
- string keys:
data["name"] - symbol keys:
data[:name]
Mixing both causes bugs.
5. Extract values into clearer variables
stats = data.dig("main_item", "stats")
a_value = stats["a"] if stats
This makes code easier to read and test.
Common Mistakes
1. Forgetting to require the JSON library
Broken code:
data = JSON.parse(string)
Fix:
require 'json'
data = JSON.parse(string)
2. Using symbol keys when the parsed hash has string keys
Broken code:
require 'json'
data = JSON.parse('{"name":"Ruby"}')
puts data[:name]
This returns nil because the key is "name", not :name.
Fix:
puts data["name"]
Or parse with symbols:
data = JSON.parse('{"name":"Ruby"}', symbolize_names: true)
puts data[:name]
3. Trying to access nested keys without checking structure
Broken code:
puts data["main_item"]["stats"]["z"]
This may return nil, and further chaining can crash.
Safer approach:
puts data.dig("main_item", "stats", "z")
4. Invalid JSON formatting
JSON requires double quotes around keys and string values.
Broken code:
string = "{desc: {someKey: 'someValue'}}"
Fix:
string = '{"desc":{"someKey":"someValue"}}'
5. Confusing JSON with Ruby hash syntax
Ruby hash syntax:
{ desc: { someKey: "someValue" } }
JSON syntax:
'{"desc":{"someKey":"someValue"}}'
They look similar, but they are not the same thing.
Comparisons
| Concept | What it is | Example | When to use |
|---|---|---|---|
| JSON string | Raw text in JSON format | '{"a":1}' | When data arrives from APIs, files, or requests |
| Parsed Ruby hash | Ruby data structure created from JSON | { "a" => 1 } | When you want to access and use the data |
| String keys | Default result from JSON.parse | data["a"] | Best when matching JSON exactly |
| Symbol keys | Optional parsing mode | data[:a] | Useful if your codebase prefers symbols |
[] vs dig
| Method | Example | Result | Best for |
|---|---|---|---|
[] | data["main_item"]["stats"]["a"] | Direct access | When you know the structure exists |
dig | data.dig("main_item", "stats", "a") | Safe nested lookup | When keys may be missing |
JSON vs Ruby hash literal
| Feature | JSON | Ruby Hash |
|---|---|---|
| Keys | Must be in double quotes | Can be strings or symbols |
| String values | Must use double quotes | Can use normal Ruby string syntax |
| Purpose | Data interchange format | Native Ruby object |
| Example | {"a":1} | { "a" => 1 } or { a: 1 } |
Cheat Sheet
require 'json'
Parse JSON
data = JSON.parse(json_string)
Access nested values
data["desc"]["someKey"]
data["main_item"]["stats"]["a"]
Safe nested access
data.dig("main_item", "stats", "a")
Parse with symbol keys
data = JSON.parse(json_string, symbolize_names: true)
data[:main_item][:stats][:a]
Handle invalid JSON
begin
data = JSON.parse(json_string)
rescue JSON::ParserError => e
puts e.message
end
Rules to remember
- JSON text must be parsed before use.
JSON.parsereturns string keys by default.- JSON uses double quotes for keys and strings.
- Use
digfor safer nested lookups. - Do not mix string keys and symbol keys unless you mean to.
Your example
require 'json'
string = '{"desc":{"someKey":"someValue","anotherKey":"value"},"main_item":{"stats":{"a":8,"b":12,"c":10}}}'
data = JSON.parse(string)
data["desc"]["someKey"] # => "someValue"
data["main_item"]["stats"]["b"] # => 12
FAQ
How do I parse a JSON string in Ruby?
Use the json library and call JSON.parse:
require 'json'
data = JSON.parse(string)
Why does data[:key] return nil after parsing JSON?
Because JSON.parse uses string keys by default. Use data["key"] or parse with symbolize_names: true.
How do I access nested JSON values in Ruby?
Use chained hash lookups:
data["main_item"]["stats"]["a"]
Or use dig for safer access:
data.dig("main_item", "stats", "a")
What happens if the JSON string is invalid?
Ruby raises JSON::ParserError. You should rescue it if the input may be malformed.
Is JSON the same as a Ruby hash?
No. JSON is a text format. A Ruby hash is a native Ruby object. JSON.parse converts JSON text into Ruby objects.
Can Ruby parse arrays from JSON too?
Yes. JSON arrays become Ruby arrays.
JSON.parse('[1, 2, 3]')
# => [1, 2, 3]
Do I always need require 'json'?
In normal Ruby scripts, yes. Some frameworks may load it for you, but it is best not to assume that.
Mini Project
Description
Build a small Ruby script that parses a JSON string representing product information and prints selected values. This demonstrates the core skills you need when receiving structured data from an API or file.
Goal
Parse nested JSON in Ruby and safely extract values from the result.
Requirements
- Store a JSON string in a Ruby variable.
- Parse the string using Ruby's JSON library.
- Print at least three nested values from the parsed data.
- Use
digfor at least one safe lookup. - Handle invalid JSON with basic error handling.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.