ZetCode

Ruby JSON

last modified October 18, 2026

JSON (JavaScript Object Notation) is a lightweight text format for exchanging data. Ruby's standard library json converts between JSON text and Ruby objects. This guide covers parsing, generating, pretty printing, nested data, file input and output, and error handling.

Parsing JSON

The json library is part of the Ruby standard library, so it only needs require 'json'. JSON.parse converts a JSON string into Ruby hashes and arrays. By default, object keys are strings; passing symbolize_names: true turns them into symbols.

parse.rb
#!/usr/bin/env ruby
require 'json'

json_str = '{"name": "Alice", "age": 30, "languages": ["Ruby", "Python"]}'

data = JSON.parse(json_str)
puts data["name"]
puts data["languages"].inspect

sym = JSON.parse(json_str, symbolize_names: true)
puts sym[:name]
puts sym.keys.inspect
puts sym[:languages].first

The first call returns a hash with string keys, so values are read with data["name"]. The second call with symbolize_names: true allows access with sym[:name]. JSON arrays become Ruby arrays.

$ ./parse.rb
Alice
["Ruby", "Python"]
Alice
[:name, :age, :languages]
Ruby

Generating JSON

JSON.generate serializes a Ruby object to a compact JSON string. The to_json method, added by the library, does the same thing directly on any object. Hashes, arrays, strings, numbers, booleans and nil are all supported.

generate.rb
#!/usr/bin/env ruby
require 'json'

hash = { name: "Bob", age: 25, active: true, scores: [90, 85.5], note: nil }

puts JSON.generate(hash)
puts hash.to_json
puts [1, "two", 3.0].to_json
puts "plain string".to_json
puts 42.to_json

Both JSON.generate(hash) and hash.to_json produce identical compact output. Symbol keys become strings, and nil becomes null. Scalar values such as strings and numbers can also be converted.

$ ./generate.rb
{"name":"Bob","age":25,"active":true,"scores":[90,85.5],"note":null}
{"name":"Bob","age":25,"active":true,"scores":[90,85.5],"note":null}
[1,"two",3.0]
"plain string"
42

Pretty printing

JSON.pretty_generate produces indented, human-readable JSON. It is useful for configuration files, logs, and debugging output.

pretty.rb
#!/usr/bin/env ruby
require 'json'

book = {
  title: "Ruby Basics",
  year: 2024,
  authors: ["Jan", "Eve"],
  publisher: { name: "ZetCode", city: "Bratislava" }
}

puts JSON.pretty_generate(book)

Nested hashes and arrays are each indented on their own lines. The structure is the same as in compact form, only the whitespace differs.

$ ./pretty.rb
{
  "title": "Ruby Basics",
  "year": 2024,
  "authors": [
    "Jan",
    "Eve"
  ],
  "publisher": {
    "name": "ZetCode",
    "city": "Bratislava"
  }
}

Nested objects and arrays

Real-world JSON often nests objects inside arrays and arrays inside objects. This example parses such a document from a heredoc and walks through it with each, sum, and dig.

nested.rb
#!/usr/bin/env ruby
require 'json'

json_str = <<~JSON
  {
    "company": "Acme",
    "employees": [
      { "name": "Ann", "skills": ["Ruby", "SQL"] },
      { "name": "Ben", "skills": ["Go"] }
    ],
    "office": { "city": "Paris", "floors": [1, 2, 3] }
  }
JSON

data = JSON.parse(json_str, symbolize_names: true)
puts data[:company]
data[:employees].each do |emp|
  puts "#{emp[:name]}: #{emp[:skills].join(', ')}"
end
puts data[:office][:floors].sum
puts data.dig(:employees, 0, :skills, 1)

Hash lookups with symbol keys reach into the nested structure. dig safely traverses a path of keys and array indexes, returning nil if any step is missing.

$ ./nested.rb
Acme
Ann: Ruby, SQL
Ben: Go
6
SQL

Reading and writing files

JSON data is commonly stored in files. File.write saves a string to disk, File.read loads it back, and JSON.parse turns the text into a Ruby structure. The file is removed at the end to keep the example self-contained.

file_io.rb
#!/usr/bin/env ruby
require 'json'

path = "data.json"
config = { host: "localhost", port: 8080, debug: false }

File.write(path, JSON.pretty_generate(config))

loaded = JSON.parse(File.read(path), symbolize_names: true)
puts "Host: #{loaded[:host]}"
puts "Port: #{loaded[:port]}"
puts File.read(path)
File.delete(path)

The configuration hash is written as pretty-printed JSON, read back with symbolized keys, and printed. The file contents match what was written.

$ ./file_io.rb
Host: localhost
Port: 8080
{
  "host": "localhost",
  "port": 8080,
  "debug": false
}

Handling invalid JSON

When the input is not valid JSON, JSON.parse raises JSON::ParserError. Wrap parsing in begin/rescue to handle malformed input gracefully. The example tries a valid string, a misspelled literal, and an unterminated array.

invalid.rb
#!/usr/bin/env ruby
require 'json'

inputs = ['{"ok": true}', '{"ok": tru}', '[1, 2,']

inputs.each do |text|
  begin
    puts JSON.parse(text).inspect
  rescue JSON::ParserError => e
    puts "Invalid JSON: #{text}"
    puts "Error class: #{e.class}"
  end
end

The valid input parses into a hash. The two malformed inputs raise JSON::ParserError, which is caught and reported instead of crashing the program.

$ ./invalid.rb
{"ok"=>true}
Invalid JSON: {"ok": tru}
Error class: JSON::ParserError
Invalid JSON: [1, 2,
Error class: JSON::ParserError

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.