JSON and XML in Ruby
Ruby ships with solid built-in support for both JSON and XML, making it easy to interchange data with web APIs, configuration files, and external services. This guide covers everything you need to work with these formats in everyday Ruby code.
JSON in Ruby
Ruby includes the JSON module in its standard library. No extra installation is required for basic use.
Parsing JSON
Use JSON.parse to convert a JSON string into Ruby objects:
require "json"
data = '{"name": "Alice", "age": 30, "active": true}'
person = JSON.parse(data)
person["name"] # => "Alice"
person["age"] # => 30
person["active"] # => true
JSON.parse raises a JSON::ParserError for malformed input, so wrap it in a rescue block when dealing with untrusted data:
begin
result = JSON.parse(dangerous_input)
rescue JSON::ParserError => e
puts "Invalid JSON: #{e.message}"
end
Generating JSON
Convert Ruby objects to JSON strings with JSON.generate or the shorter #to_json method:
require "json"
data = { name: "Bob", scores: [95, 87, 92] }
JSON.generate(data) # => "{\"name\":\"Bob\",\"scores\":[95,87,92]}"
data.to_json # => "{\"name\":\"Bob\",\"scores\":[95,87,92]}"
Both produce identical output. Use whichever reads better in context.
Pretty Printing
For human-readable output, pass the indent option:
data = { user: "Carol", roles: ["admin", "editor"] }
puts JSON.pretty_generate(data)
# {
# "user": "Carol",
# "roles": [
# "admin",
# "editor"
# ]
# }
This is especially useful when writing configuration files or debugging API responses.
Custom Object Serialization
When you need to serialize custom objects, implement to_json on your class:
require "json"
class Point
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
def to_json(*_args)
{ x: @x, y: @y }.to_json
end
end
point = Point.new(3, 4)
point.to_json # => "{\"x\":3,\"y\":4}"
JSON with Symbols
By default, JSON keys become strings in Ruby. If you prefer symbol keys, use the symbolize_names option:
data = '{"count": 42}'
parsed = JSON.parse(data, symbolize_names: true)
parsed[:count] # => 42 (Symbol, not String)
XML in Ruby
Ruby offers two main approaches for working with XML: REXML, which ships with the standard library, and Nokogiri, a popular gem with a more powerful API.
REXML: Built-in XML Parsing
REXML is part of Ruby’s standard library and handles both tree-based and stream-based parsing.
Parsing a Document
require "rexml/document"
xml_string = <<~XML
<users>
<user id="1">
<name>Alice</name>
<email>alice@example.com</email>
</user>
<user id="2">
<name>Bob</name>
<email>bob@example.com</email>
</user>
</users>
XML
doc = REXML::Document.new(xml_string)
# Get all user names
doc.elements.each("users/user/name") do |element|
puts element.text
end
# Alice
# Bob
Extracting Attributes
Access element attributes with attributes:
doc.elements.each("users/user") do |user|
puts "ID: #{user.attributes['id']}, Name: #{user.elements['name'].text}"
end
# ID: 1, Name: Alice
# ID: 2, Name: Bob
Building XML
Create XML documents programmatically with REXML::Element:
require "rexml/document"
require "rexml/formatters/pretty"
root = REXML::Element.new("config")
root.add_attribute("version", "1.0")
database = root.add_element("database")
database.add_element("host").text = "localhost"
database.add_element("port").text = "5432"
formatter = REXML::Formatters::Pretty.new
formatter.write(root, $stdout)
# <config version='1.0'>
# <database>
# <host>localhost</host>
# <port>5432</port>
# </database>
# </config>
Nokogiri: Faster and More Powerful
Nokogiri is a gem that wraps native XML parsers (libxml2 and libxslt). It is faster than REXML and the de facto standard for XML processing in Ruby on Rails applications.
Install it with:
gem install nokogiri
Parsing HTML or XML
require "nokogiri"
html = <<~HTML
<html>
<body>
<article>
<h1>Hello World</h1>
<p class="intro">Welcome to the site.</p>
<p>More content here.</p>
</article>
</body>
</html>
HTML
doc = Nokogiri::HTML(html)
# Find elements with CSS selectors
doc.css("h1").text # => "Hello World"
doc.css("p.intro").text # => "Welcome to the site."
doc.css("p").map(&:text) # => ["Welcome to the site.", "More content here."]
XPath Queries
For precise element selection, use XPath:
doc = Nokogiri::XML(xml_string)
# Select all user elements
doc.xpath("//user").each do |node|
puts node.at_xpath("name").text
end
Searching with Namespaces
Nokogiri handles XML namespaces cleanly:
xml = <<~XML
<feed xmlns="https://example.org/feed/1.0">
<entry>
<title>Sample Post</title>
</entry>
</feed>
XML
doc = Nokogiri::XML(xml)
ns = { "f" => "https://example.org/feed/1.0" }
title = doc.at_xpath("//f:title", ns)
puts title.text # => "Sample Post"
Building Documents
Nokogiri also excels at creating new XML documents:
require "nokogiri"
builder = Nokogiri::XML::Builder.new do |xml|
xml.products {
xml.product(id: "p1") {
xml.name("Widget")
xml.price("29.99")
}
xml.product(id: "p2") {
xml.name("Gadget")
xml.price("49.99")
}
}
end
puts builder.to_xml
# <?xml version="1.0"?>
# <products>
# <product id="p1">
# <name>Widget</name>
# <price>29.99</price>
# </product>
# <product id="p2">
# <name>Gadget</name>
# <price>49.99</price>
# </product>
# </products>
Performance Considerations
JSON Speed
The standard json gem is pure Ruby and works everywhere, but it is not the fastest option. For high-throughput scenarios, consider these alternatives:
| Gem | Description |
|---|---|
oj | Optimized JSON parser, 3-10x faster than the stdlib |
yajl-ruby | Streaming JSON parser, low memory footprint |
JSON (stdlib) | Convenient, fully compatible, moderate speed |
Using oj is as simple as:
require "oj"
data = Oj.load(json_string) # parse
output = Oj.dump(ruby_object) # generate
XML Speed
For XML processing, Nokogiri consistently outperforms REXML because it uses compiled C libraries. Prefer Nokogiri when:
- Processing large documents (megabytes of XML)
- Parsing HTML from web pages
- Performance is critical in your application
Use REXML for quick scripts, small documents, or when you want to avoid adding gem dependencies.
Memory Usage
Both JSON and XML parsers hold entire documents in memory. For very large files, consider:
- Streaming XML parsers like
Nokogiri::XML::Reader— processes node by node - Line-delimited JSON (JSON Lines) — parse one JSON object per line without loading everything at once
Choosing Between JSON and XML
| Factor | JSON | XML |
|---|---|---|
| Typical use | Web APIs, config files | Document formats, SOAP services |
| Verbosity | Compact | Verbose with tags |
| Schema support | None (schema-less) | XSD, DTD support |
| Ruby stdlib | Yes (JSON) | Yes (REXML) |
| Recommended for large docs | oj gem | Nokogiri |
For most modern web APIs, JSON is the default choice. XML remains relevant for legacy enterprise systems, document formats like RSS/Atom, and scenarios requiring formal schema validation.
Common Pitfalls
JSON:
- forgetting to
require "json"— the module is not auto-loaded - mixing up
JSON.parse(string to object) andJSON.generate(object to string) - not handling
JSON::ParserErrorwhen parsing external input
XML:
- REXML does not handle malformed HTML well — use Nokogiri for HTML
- Nokogiri’s XPath expressions are case-sensitive; double-check element names
- Namespace prefixes can break queries if not declared explicitly
See Also
- /reference/modules/json/ — Full JSON module reference with all methods
- /reference/modules/yaml/ — YAML parsing as an alternative to JSON for config files
- /guides/ruby-working-with-strings/ — String manipulation techniques used when processing JSON and XML data