Skip to content

Instantly share code, notes, and snippets.

@peterc
Created April 14, 2025 17:41
Show Gist options
  • Select an option

  • Save peterc/413c94b211b3ec4cf975ae2d36188c16 to your computer and use it in GitHub Desktop.

Select an option

Save peterc/413c94b211b3ec4cf975ae2d36188c16 to your computer and use it in GitHub Desktop.

Revisions

  1. peterc created this gist Apr 14, 2025.
    272 changes: 272 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,272 @@
    # 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:

    ```ruby
    require 'json'
    ```

    To use additional JSON extensions, require their files:

    ```ruby
    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

    ```ruby
    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:

    ```ruby
    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:

    ```ruby
    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:

    ```ruby
    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:

    ```ruby
    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`:

    ```ruby
    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`:

    ```ruby
    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:

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

    ---

    ### Generating Options

    Generate JSON with formatting options using:

    ```ruby
    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:

    ```ruby
    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`:

    ```shell
    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.