JSON and YAML parsing in Ruby
Fast recall
Quick revision
Core answer
Whether the learner distinguishes data format, Ruby object mapping, parse errors, and safe YAML loading.
Using YAML.load on untrusted text or assuming JSON round-trips symbols and custom Ruby objects.
Explain string keys, allowed YAML classes, schema validation, and format choice by boundary.
Interview-ready guide
Interview answer
JSON and YAML are text formats that Ruby can parse into arrays, hashes, strings, numbers, booleans, and nil-like values. JSON is a narrow, widely interoperable format for APIs. YAML is more human-oriented and supports comments, aliases, and richer type conventions, so it is common in configuration.
With require 'json', JSON.generate(value) creates JSON and JSON.parse(text) reads it. JSON object keys become Ruby strings by default. symbolize_names: true is convenient for trusted, bounded structures, but creating symbols from uncontrolled keys needs deliberate limits and consistency.
With require 'yaml', I use YAML.safe_load for content that may be edited or supplied outside trusted source code. It accepts basic scalar and collection classes by default and rejects other Ruby classes and aliases unless the application explicitly permits them.
For example, an API response can be parsed as JSON, then checked for required id and name fields and expected types. A YAML deployment file can be safely loaded with string keys and validated against a known environment schema before any setting is applied.
Parsing handles syntax and representation, not trust or business correctness. I catch JSON::ParserError or Psych::SyntaxError at the boundary, limit input size and nesting where needed, validate the resulting shape, and avoid logging secrets from malformed configuration or payloads. Output generation follows the same rule: serialize an intentional public shape rather than an arbitrary Ruby object graph.
JSON and YAML parsing in Ruby
Direct Answer
Use JSON.generate or JSON.dump and JSON.parse for interoperable data, remembering that object keys parse as strings unless symbolize_names is requested. Use YAML.safe_load for untrusted or externally editable YAML, permitting only required classes and aliases, because general YAML deserialization can construct Ruby-specific objects.
Deep Dive & Architecture
JSON has a deliberately small data model: objects with string member names, arrays, strings, numbers, booleans, and null. Ruby hashes can have symbol or object keys and can contain arbitrary objects, so a JSON round trip is not an identity operation. Symbol keys become JSON object names and parse back as strings by default; Time, Date, Decimal, and application objects need an explicit representation.
JSON.parse can create symbol keys with symbolize_names: true, but one project should choose a consistent boundary convention. String keys match the JSON model and avoid converting arbitrary external field names into process-wide identifiers on older Ruby implementations. Parse errors should produce a controlled client or configuration failure rather than a generic nil that later crashes far from the boundary.
YAML can express anchors, aliases, tagged values, and language-specific objects. That flexibility is useful for trusted internal files but dangerous for untrusted deserialization. YAML.safe_load allows basic classes and rejects unapproved classes. Permit a class such as Date only when the schema genuinely needs it, and enable aliases only when the expected format uses them and resource limits are considered.
Safe deserialization does not make a configuration correct. After parsing, verify the top-level type, permitted keys, required values, enum choices, numeric ranges, URL schemes, and any relationship between fields. For an API, cap request size before parsing and avoid nesting that can exhaust memory or CPU. For configuration, include a filename in error reporting and fail startup with a clear message rather than running with guessed defaults.
Choose the format by boundary. JSON is predictable for public web clients and has broad tooling. YAML is readable for controlled configuration but whitespace-sensitive and more complex. Do not use either as a secure object store or authorization mechanism. Convert domain objects to an intentional data shape on output, validate that shape on input, and keep secrets out of example files and parser error logs.
Round-trip tests are useful but limited: they prove the chosen values survive one encoder and decoder, not that another language interprets custom extensions identically. Interoperable boundaries should stay within the format's standard data model and publish examples clients can validate.
Code Example
require "json"
require "yaml"
payload = { "name" => "Ada", "skills" => ["Ruby", "SQL"] }
json = JSON.generate(payload)
parsed_json = JSON.parse(json)
raise "missing name" unless parsed_json.fetch("name").is_a?(String)
yaml = <<~YAML
environment: production
workers: 4
YAML
config = YAML.safe_load(yaml)
raise "bad workers" unless config.fetch("workers").is_a?(Integer)
Both parsers return basic Ruby values; explicit checks then enforce the application's expected shape.
Common Mistakes & Pitfalls
Using YAML.load on untrusted text or assuming JSON round-trips symbols and custom Ruby objects.
Check your understanding
Answer these without looking back at the article.
- Why does JSON.parse normally return string keys?
- What does YAML.safe_load reject by default?
- Why is schema validation still needed after safe parsing?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.