JSON & YAML
Reviewed & published by Brayan K
JSON and YAML are two text formats for serializing data, and Ruby's standard library converts between them and native hashes and arrays with a single method call in each direction.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
You'll parse and generate JSON, symbolize keys, and round-trip a hash through YAML.
What You'll Learn in This Lesson
- require 'json' and JSON.parse into Ruby data
- JSON.generate, to_json, and pretty_generate
- symbolize_names for symbol keys
- require 'yaml' for config-style data
- YAML.dump and YAML.load
- Round-tripping a hash safely
1️⃣ Parsing JSON into Ruby
require 'json', then JSON.parse(string) returns a Hash/Array tree. By default object keys come back as strings; pass symbolize_names: true to get symbol keys, which read more naturally as data[:name].
require 'json'
# Parse a JSON string into Ruby data (hash + array).
text = '{"name": "Ada", "langs": ["ruby", "c"], "active": true}'
data = JSON.parse(text)
puts data.class # Hash
puts data["name"] # keys are STRINGS by default
puts data["langs"].inspect
puts data["active"]
# symbolize_names: true turns keys into symbols instead.
sym = JSON.parse(text, symbolize_names: true)
puts sym[:name] # now access with a symbol2️⃣ Generating JSON from Ruby
JSON.generate(obj) and the shortcut obj.to_json produce a compact string. For human-readable output with indentation, use JSON.pretty_generate. Notice symbol keys come out as plain JSON strings.
require 'json'
person = { name: "Ada", age: 36, langs: ["ruby", "c"] }
# generate / to_json produce a compact JSON string:
puts JSON.generate(person)
puts person.to_json
# pretty_generate adds indentation for humans:
puts JSON.pretty_generate(person)3️⃣ YAML: Dump, Load & Round-Trip
YAML is the go-to for human-edited config. require 'yaml', then YAML.dump(obj) writes a YAML string (note the leading --- document marker) and YAML.load(string) reads it back — a clean round-trip.
require 'yaml'
config = {
"app" => "blog",
"port" => 3000,
"features"=> ["search", "comments"],
"debug" => false
}
# dump: Ruby -> YAML text
yaml = YAML.dump(config)
puts yaml
# load: YAML text -> Ruby (round-trip)
back = YAML.load(yaml)
puts "round-trips equal? #{back == config}"
puts back["features"].inspectYour turn. Parse a small JSON object using symbol keys and read two values. Fill in each ___.
require 'json'
# 🎯 YOUR TURN — replace each ___ then run with: ruby place.rb
text = '{"city": "Paris", "visited": false}'
# Parse with SYMBOL keys so we can use [:city] not ["city"]:
data = JSON.parse(text, ___: true) # 👉 the option that symbolizes keys
puts data[:city]
puts data[:___] # 👉 read the "visited" value
# ✅ Expected output:
# Paris
# falseMini-Challenge: Process a Users Feed
Parse a JSON array of users, filter the admins, and emit a JSON summary. This parse → transform → generate flow is the heart of working with APIs. Run with ruby users.rb.
# 🎯 MINI-CHALLENGE: process a users feed.
# Save as users.rb and run with: ruby users.rb
#
# 1. require 'json'.
# 2. Parse a JSON array of user objects with symbolize_names: true.
# 3. Select the admins and collect their names.
# 4. Build a summary hash { total:, admin_count: } and print it as JSON.
#
# ✅ Example output:
# Admins: Ada, Cy
# {"total":3,"admin_count":2}
# your code hereCommon Errors (and the fix)
- "unexpected token" on parse — ❌ The string isn't valid JSON (single quotes, trailing comma). ✅ JSON requires double quotes and no trailing commas.
- nil when reading a key — ❌ You used a symbol key on a string-keyed hash. ✅ Match the parse option: strings by default, symbols with symbolize_names: true.
- "uninitialized constant JSON/YAML" — ❌ Missing require. ✅ Add require 'json' or require 'yaml'.
- Symbol keys in JSON output — ❌ Expecting symbols to survive. ✅ JSON has no symbols; they always become strings.
Pro Tips
- 💡 Pretty for humans: use JSON.pretty_generate when writing files people will read.
- 💡 Safe YAML: for untrusted input use YAML.safe_load and whitelist any classes you allow.
- 💡 JSON ⊂ YAML: a YAML loader can read JSON, since JSON is a subset of YAML.
📋 Quick Reference — JSON & YAML
| Task | Ruby |
|---|---|
| Parse JSON | JSON.parse(str) |
| Symbol keys | symbolize_names: true |
| To JSON | obj.to_json |
| Pretty JSON | JSON.pretty_generate(obj) |
| Dump YAML | YAML.dump(obj) |
| Load YAML | YAML.load(str) |
🎉 Lesson Complete!
- ✅ JSON.parse → Ruby; to_json → string
- ✅ symbolize_names: true for symbol keys
- ✅ pretty_generate for readable output
- ✅ YAML.dump/load round-trips a hash
- ✅ JSON for APIs, YAML for human-edited config
Practice quiz
What must you require before using JSON methods?
- require 'parser'
- import json
- require 'json'
- include JSON
Answer: require 'json'. require 'json' loads the standard-library JSON module.
What does JSON.parse return for a JSON object?
- a Hash
- a String
- an Array
- a Struct
Answer: a Hash. A JSON object parses into a Ruby Hash (arrays become Arrays).
By default, JSON.parse gives object keys as:
- symbols
- integers
- nil
- strings
Answer: strings. Keys come back as strings unless you request symbols.
Which option makes JSON.parse return symbol keys?
- symbols: true
- symbolize_names: true
- as_symbols: true
- keys: :symbol
Answer: symbolize_names: true. Pass symbolize_names: true to get symbol keys like data[:name].
Which produces a compact JSON string from a Ruby object?
- obj.to_json
- obj.to_s
- obj.dump
- obj.inspect
Answer: obj.to_json. to_json (or JSON.generate) produces a compact JSON string.
Which method produces indented, human-readable JSON?
- JSON.generate
- JSON.format
- JSON.pretty_generate
- JSON.indent
Answer: JSON.pretty_generate. JSON.pretty_generate adds indentation for readability.
What happens to Ruby symbols when converted to JSON?
- they stay symbols
- they become strings
- they raise an error
- they become nil
Answer: they become strings. JSON has no symbol type, so symbols become strings.
Which method serializes a Ruby object to YAML text?
- YAML.parse
- YAML.write
- YAML.to_yaml
- YAML.dump
Answer: YAML.dump. YAML.dump converts a Ruby object into a YAML string.
What marker begins a YAML document produced by YAML.dump?
- ===
- ---
- %YAML
- #yaml
Answer: ---. YAML documents begin with the --- document marker.
For untrusted YAML input you should prefer:
- YAML.load with no limits
- eval
- YAML.safe_load
- JSON.parse
Answer: YAML.safe_load. safe_load only allows basic types, avoiding object-injection risks.
Continue this course
- Previous: Dates & Times
- Next: Testing with Minitest — Write assertions, setup, and assert_raises with the built-in Minitest
- Quick reference: Ruby cheat sheet
Frequently asked questions
What's the difference between JSON.parse and JSON.generate?
JSON.parse takes a JSON string and returns Ruby objects — a JSON object becomes a Hash, an array becomes an Array, true/false/null become true/false/nil. JSON.generate (and the convenient obj.to_json) goes the other direction, turning Ruby data into a compact JSON string. Together they let you read data from APIs or files and write it back out in a portable format.
Why are my JSON keys strings instead of symbols?
JSON has no concept of symbols, so JSON.parse defaults to string keys, meaning you access values with data["name"]. Pass symbolize_names: true to get symbol keys (data[:name]) instead, which is often nicer in Ruby code. Be cautious symbolizing keys from untrusted input on very old Ruby versions, but on modern Ruby symbols are garbage-collected so it's safe.
When should I use YAML instead of JSON?
JSON is the lingua franca for web APIs and data interchange — compact and supported everywhere. YAML is more human-friendly with its indentation and lack of braces, which is why it dominates configuration files (think .yml CI configs and Rails settings). Use JSON for machine-to-machine data and YAML for files humans edit by hand. Ruby's YAML.load even parses JSON, since JSON is a subset of YAML.
Is it safe to load arbitrary YAML?
Be careful. The full YAML loader can instantiate Ruby objects, which is a security risk with untrusted input. Modern Ruby's YAML.load is safe by default (it only allows basic types), and YAML.safe_load lets you whitelist permitted classes explicitly. For data you don't control, prefer safe_load and never use the unsafe loader. For your own trusted config files, plain load/dump is fine.