Skip to content

Instantly share code, notes, and snippets.

@peterc
Created April 14, 2025 17:41
Show Gist options
  • Save peterc/413c94b211b3ec4cf975ae2d36188c16 to your computer and use it in GitHub Desktop.
Save peterc/413c94b211b3ec4cf975ae2d36188c16 to your computer and use it in GitHub Desktop.
GPT-4.1-mini generated documentation for Ruby's JSON library

JSON Library for Ruby

The JSON library is a comprehensive and robust solution for parsing, generating, and manipulating JSON data in Ruby. It offers full support for JSON encoding and decoding, including extensions for common Ruby types like Date, Time, Complex, Rational, and more. The library also provides advanced features such as extended JSON object creation, custom generators, and parser configurations.

This README provides an overview of usage, advanced options, compatible Ruby types, and important notes for developers who want to integrate JSON handling seamlessly in their Ruby applications.


Features

  • Encode and decode JSON data reliably
  • Support for core Ruby types and standard library extensions:
    • Core classes: Time, Date, DateTime, Range, Struct, Regexp
    • Standard library classes: Complex, Rational, BigDecimal, Set, OpenStruct
  • Extended JSON: serialize and deserialize custom Ruby objects using json_class
  • Custom JSON coders with configurable options
  • Fine control of parser and generator behaviors (encoding, error handling, indentation, circular reference checking)
  • Support for strict and non-strict JSON generation modes
  • Allows customization for ASCII-only output or UTF-8 with escapes
  • Support for frozen string optimization and symbolized keys on decode
  • Safe loading with deprecated support for legacy create_additions
  • Extensive test suite ensuring correctness and compatibility in multiple scenarios

Installation

This library is typically part of the Ruby standard library. You can use it directly without further installation:

require 'json'

To use additional JSON extensions, require their files:

require 'json/add/core'
require 'json/add/complex'
require 'json/add/rational'
require 'json/add/bigdecimal' # if you use BigDecimal
require 'json/add/ostruct'    # if you use OpenStruct
require 'json/add/set'        # if you use Set

Usage

Basic Encoding and Decoding

require 'json'

# Encoding Ruby objects to JSON strings
hash = { name: "Alice", age: 30, interests: ["Reading", "Hiking"] }
json_string = JSON.generate(hash)
puts json_string
# => {"name":"Alice","age":30,"interests":["Reading","Hiking"]}

# Decoding JSON strings to Ruby objects
parsed_hash = JSON.parse(json_string)
puts parsed_hash["name"]  # => Alice

You can also use the shorthand indexed calls:

json_string = JSON[hash]       # same as JSON.generate(hash)
parsed_hash = JSON[json_string] # same as JSON.parse(json_string)

Extended JSON - Serializing Custom Ruby Objects

To serialize and deserialize custom Ruby objects, define to_json and json_create methods:

class MyClass
  attr_reader :attr

  def initialize(attr)
    @attr = attr
  end

  def to_json(*args)
    { JSON.create_id => self.class.name, 'args' => [@attr] }.to_json(*args)
  end

  def self.json_create(object)
    new(*object['args'])
  end
end

obj = MyClass.new(123)
json = JSON.generate(obj)
obj2 = JSON.parse(json, create_additions: true)

puts obj2.is_a?(MyClass)  # => true
puts obj2.attr            # => 123

If create_additions is disabled, the parsed JSON will be a Hash instead of your object:

obj_hash = JSON.parse(json, create_additions: false)
puts obj_hash # => {"json_class"=>"MyClass", "args"=>[123]}

Handling Special Ruby Types

To handle special types, require the corresponding files:

require 'json/add/complex'    # Support Complex numbers
require 'json/add/rational'   # Support Rational numbers
require 'json/add/bigdecimal' # Support BigDecimal numbers
require 'json/add/ostruct'    # Support OpenStruct objects
require 'json/add/set'        # Support Set collections

Example with Rational:

require 'json/add/rational'

r = Rational(2, 3)
json = JSON.generate(r)
r2 = JSON.parse(json, create_additions: true)
puts r2 == r  # => true

JSON Coders with Custom Behaviors

You can create custom JSON encoder/decoder instances using JSON::Coder:

coder = JSON::Coder.new(symbolize_names: true) do |object|
  # Custom dump implementation or fallback return value
  object.to_s
end

json = coder.dump({ "a" => 1 })
obj = coder.load(json)
puts obj  # => {:a=>1}

Parsing Options

You can configure JSON parsing options:

  • symbolize_names: true to convert JSON keys to Ruby symbols
  • create_additions: true to create Ruby objects from extended JSON (json_class)
  • allow_nan: true to allow NaN and Infinity values during parsing
  • allow_trailing_comma: true to support dangling commas in arrays and objects
  • freeze: true freezes strings and arrays on creation to save memory

Example:

json = '{"foo": "bar"}'
parsed = JSON.parse(json, symbolize_names: true)
puts parsed[:foo]  # => "bar"

Generating Options

Generate JSON with formatting options using:

options = {
  ascii_only: true,     # Escape non-ASCII characters
  indent: '  ',         # Indentation string
  space: ' ',           # Space after colon
  space_before: '',     # Space before colon
  object_nl: "\n",      # Newline after object elements
  array_nl: "\n",       # Newline after array elements
  max_nesting: 100,     # Max depth for nested structures
  allow_nan: false,     # Allow NaN, Infinity
  strict: false,        # Strict JSON generation
  script_safe: false    # Escape script-breaking characters
}

json_pretty = JSON.generate(obj, options)

Use JSON.pretty_generate(obj) for a standard pretty-printed output.


Working with Strings and Encodings

The library handles various string encodings robustly, supporting UTF-8, UTF-16, UTF-32, and ASCII-8BIT binary data conversions transparently.

To generate ASCII-only JSON encoding, use:

json = JSON.generate("© ≠ €!", ascii_only: true)
# => "\"\\u00a9 \\u2260 \\u20ac!\""

Error Handling

  • JSON::ParserError is raised for malformed or invalid JSON inputs.
  • JSON::GeneratorError is raised for unsupported serialization cases, invalid encodings, or circular references exceeding max nesting.
  • JSON::NestingError is raised when the maximum allowed nesting depth is exceeded during parsing or generation.

Thread Safety and Performance

  • Supports safe use with Ruby's Ractor and multiprocessing (tested on fork).
  • Options or states passed for JSON generation are immutable during the operation.
  • Implements circular reference detection in nested objects to prevent stack overflows.

Deprecated Features

  • Legacy JSON.load with create_additions has been deprecated in favor of safer APIs such as JSON.unsafe_load.

Testing

The library includes an extensive test suite covering:

  • Core parsing and generation of various JSON structures
  • Extended JSON support for custom classes and Ruby standard classes
  • Support for multiple string encodings and edge cases
  • Parser error messages and robustness tests with malformed data fixtures
  • Custom JSON coders and generator states
  • Compatibility with various Ruby features like OpenStruct, BigDecimal, Set
  • Ractor and multi-process safety

Run the test suite with Ruby's built-in Test::Unit:

ruby -Ilib:test test/json/json_parser_test.rb

Or use your preferred Ruby test runner for all tests.


Contributing

Bug reports and pull requests are welcome. Please ensure tests pass and adhere to the established test coverage when adding features or fixing bugs.


License

[Specify your license here, if applicable.]


Summary

The JSON Ruby library is a mature and highly capable JSON serialization and deserialization tool that elegantly handles Ruby's object model and common data structures. It offers advanced configuration and robust error handling, suitable for a wide range of applications needing JSON processing in Ruby.

Explore the tests and documentation to leverage all capabilities and integrate seamlessly into your Ruby projects.

@peterc
Copy link
Author

peterc commented Apr 14, 2025

Note that it does not realize that JSON is a gem because it was only fed the test suite and not the main README, so it does not know about things that the test suite does not test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment