JSON and XML in Ruby

· 5 min read · Updated March 31, 2026 · intermediate
ruby json xml stdlib guides

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:

GemDescription
ojOptimized JSON parser, 3-10x faster than the stdlib
yajl-rubyStreaming 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

FactorJSONXML
Typical useWeb APIs, config filesDocument formats, SOAP services
VerbosityCompactVerbose with tags
Schema supportNone (schema-less)XSD, DTD support
Ruby stdlibYes (JSON)Yes (REXML)
Recommended for large docsoj gemNokogiri

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) and JSON.generate (object to string)
  • not handling JSON::ParserError when 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