123456789_123456789_123456789_123456789_123456789_

Module: JSON

Overview

Symbol:

require 'json/add/symbol'
ruby0 = :foo # foo
json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
ruby1 = JSON.parse(json, create_additions: true) # foo
ruby1.class # Symbol

Time:

require 'json/add/time'
ruby0 = Time.now # 2020-05-02 11:28:26 -0500
json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
ruby1.class # Time

Custom JSON Additions

In addition to the JSON additions provided, you can craft JSON additions of your own, either for Ruby built-in classes or for user-defined classes.

Here's a user-defined class Foo:

class Foo
  attr_accessor :bar, :baz
  def initialize(bar, baz)
    self.bar = bar
    self.baz = baz
  end
end

Here's the JSON addition for it:

# Extend class Foo with JSON addition.
class Foo
  # Serialize Foo object with its class name and arguments
  def to_json(*args)
    {
      JSON.create_id  => self.class.name,
      'a'             => [ bar, baz ]
    }.to_json(*args)
  end
  # Deserialize JSON string by constructing new Foo object with arguments.
  def self.json_create(object)
    new(*object['a'])
  end
end

Demonstration:

require 'json'
# This Foo object has no custom addition.
foo0 = Foo.new(0, 1)
json0 = JSON.generate(foo0)
obj0 = JSON.parse(json0)
# Lood the custom addition.
require_relative 'foo_addition'
# This foo has the custom addition.
foo1 = Foo.new(0, 1)
json1 = JSON.generate(foo1)
obj1 = JSON.parse(json1, create_additions: true)
#   Make a nice display.
display = <<EOT
Generated JSON:
  Without custom addition:  #{json0} (#{json0.class})
  With custom addition:     #{json1} (#{json1.class})
Parsed JSON:
  Without custom addition:  #{obj0.inspect} (#{obj0.class})
  With custom addition:     #{obj1.inspect} (#{obj1.class})
EOT
puts display

Output:

Generated JSON:
  Without custom addition:  "#<Foo:0x0000000006534e80>" (String)
  With custom addition:     {"json_class":"Foo","a":[0,1]} (String)
Parsed JSON:
  Without custom addition:  "#<Foo:0x0000000006534e80>" (String)
  With custom addition:     #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)

Constant Summary

Class Attribute Summary

  • .create_id rw

    Sets or returns create identifier, which is used to decide if the json_create hook of a class should be called; initial value is json_class:

  • .dump_default_options rw

    Sets or returns the default options for the .dump method.

  • .generator rw

    Returns the JSON generator module that is used by JSON.

  • .load_default_options rw

    Sets or returns default options for the .load method.

  • .parser rw

    Returns the JSON parser class that is used by JSON.

  • .state rw

    Sets or Returns the JSON generator state class that is used by JSON.

  • .generator=(generator) rw Internal use only

    ::Set the module generator to be used by JSON.

  • .parser=(parser) rw Internal use only

    ::Set the JSON parser class parser to be used by JSON.

Class Method Summary

Class Attribute Details

.create_id (rw)

Sets or returns create identifier, which is used to decide if the json_create hook of a class should be called; initial value is json_class:

JSON.create_id # => 'json_class'
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 105

attr_accessor :create_id

.dump_default_options (rw)

Sets or returns the default options for the .dump method. Initially:

opts = JSON.dump_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 575

attr_accessor :dump_default_options

.generator (rw)

Returns the JSON generator module that is used by JSON. This is either ::JSON::Ext::Generator or JSON::Pure::Generator:

JSON.generator # => JSON::Ext::Generator
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 95

attr_reader :generator

.generator=(generator) (rw)

This method is for internal use only.

::Set the module generator to be used by JSON.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 58

def generator=(generator) # :nodoc:
  old, $VERBOSE = $VERBOSE, nil
  @generator = generator
  generator_methods = generator::GeneratorMethods
  for const in generator_methods.constants
    klass = deep_const_get(const)
    modul = generator_methods.const_get(const)
    klass.class_eval do
      instance_methods(false).each do |m|
        m.to_s == 'to_json' and remove_method m
      end
      include modul
    end
  end
  self.state = generator::State
  const_set :State, self.state
  const_set :SAFE_STATE_PROTOTYPE, State.new
  const_set :FAST_STATE_PROTOTYPE, State.new(
    :indent         => '',
    :space          => '',
    :object_nl      => "",
    :array_nl       => "",
    :max_nesting    => false
  )
  const_set :PRETTY_STATE_PROTOTYPE, State.new(
    :indent         => '  ',
    :space          => ' ',
    :object_nl      => "\n",
    :array_nl       => "\n"
  )
ensure
  $VERBOSE = old
end

.load_default_options (rw)

Sets or returns default options for the .load method. Initially:

opts = JSON.load_default_options
opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 399

attr_accessor :load_default_options

.parser (rw)

Returns the JSON parser class that is used by JSON. This is either ::JSON::Ext::Parser or JSON::Pure::Parser:

JSON.parser # => JSON::Ext::Parser
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 29

attr_reader :parser

.parser=(parser) (rw)

This method is for internal use only.

::Set the JSON parser class parser to be used by JSON.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 32

def parser=(parser) # :nodoc:
  @parser = parser
  remove_const :Parser if const_defined?(:Parser, false)
  const_set :Parser, parser
end

.state (rw)

Sets or Returns the JSON generator state class that is used by JSON. This is either ::JSON::Ext::Generator::State or JSON::Pure::Generator::State:

JSON.state # => JSON::Ext::Generator::State
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 100

attr_accessor :state

Class Method Details

.[](object) ⇒ Array, String

If object is a String, calls .parse with object and opts (see method #parse):

json = '[0, 1, null]'
JSON[json]# => [0, 1, nil]

Otherwise, calls .generate with object and opts (see method #generate):

ruby = [0, 1, nil]
JSON[ruby] # => '[0,1,null]'
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 18

def [](object, opts = {})
  if object.respond_to? :to_str
    JSON.parse(object.to_str, opts)
  else
    JSON.generate(object, opts)
  end
end

.deep_const_get(path)

This method is for internal use only.

Return the constant located at path. The format of path has to be either ::A::B::C or A::B::C. In any case, A has to be located at the top level (absolute namespace path?). If there doesn't exist a constant at the given path, an ArgumentError is raised.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 42

def deep_const_get(path) # :nodoc:
  path.to_s.split(/::/).inject(Object) do |p, c|
    case
    when c.empty?                  then p
    when p.const_defined?(c, true) then p.const_get(c)
    else
      begin
        p.const_missing(c)
      rescue NameError => e
        raise ArgumentError, "can't get const #{path}: #{e}"
      end
    end
  end
end

.dump(obj, io = nil, limit = nil) (mod_func)

Dumps obj as a JSON string, i.e. calls generate on the object and returns the result.

The default options can be changed via method .dump_default_options.

  • Argument io, if given, should respond to method write; the JSON String is written to io, and io is returned. If io is not given, the JSON String is returned.

  • Argument limit, if given, is passed to .generate as option max_nesting.


When argument io is not given, returns the JSON String generated from obj:

obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
json = JSON.dump(obj)
json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"

When argument io is given, writes the JSON String to io and returns io:

path = 't.json'
File.open(path, 'w') do |file|
  JSON.dump(obj, file)
end # => #<File:t.json (closed)>
puts File.read(path)

Output:

{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 610

def dump(obj, anIO = nil, limit = nil)
  if anIO and limit.nil?
    anIO = anIO.to_io if anIO.respond_to?(:to_io)
    unless anIO.respond_to?(:write)
      limit = anIO
      anIO = nil
    end
  end
  opts = JSON.dump_default_options
  opts = opts.merge(:max_nesting => limit) if limit
  result = generate(obj, opts)
  if anIO
    anIO.write result
    anIO
  else
    result
  end
rescue JSON::NestingError
  raise ArgumentError, "exceed depth limit"
end

.fast_generate(obj, opts) ⇒ String (mod_func) Also known as: .fast_unparse

Arguments obj and opts here are the same as arguments obj and opts in .generate.

By default, generates JSON data without checking for circular references in obj (option max_nesting set to false, disabled).

Raises an exception if obj contains circular references:

a = []; b = []; a.push(b); b.push(a)
# Raises SystemStackError (stack level too deep):
JSON.fast_generate(a)
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 314

def fast_generate(obj, opts = nil)
  if State === opts
    state, opts = opts, nil
  else
    state = FAST_STATE_PROTOTYPE.dup
  end
  if opts
    if opts.respond_to? :to_hash
      opts = opts.to_hash
    elsif opts.respond_to? :to_h
      opts = opts.to_h
    else
      raise TypeError, "can't convert #{opts.class} into Hash"
    end
    state.configure(opts)
  end
  state.generate(obj)
end

.fast_unparse (mod_func)

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 335

alias fast_unparse fast_generate

.generate(obj, opts = nil) ⇒ String (mod_func) Also known as: .unparse

Returns a String containing the generated JSON data.

See also .fast_generate, .pretty_generate.

Argument obj is the Ruby object to be converted to JSON.

Argument opts, if given, contains a Hash of options for the generation. See Generating Options.


When obj is an Array, returns a String containing a JSON array:

obj = ["foo", 1.0, true, false, nil]
json = JSON.generate(obj)
json # => '["foo",1.0,true,false,null]'

When obj is a Hash, returns a String containing a JSON object:

obj = {foo: 0, bar: 's', baz: :bat}
json = JSON.generate(obj)
json # => '{"foo":0,"bar":"s","baz":"bat"}'

For examples of generating from other Ruby objects, see Generating JSON from Other Objects.


Raises an exception if any formatting option is not a String.

Raises an exception if obj contains circular references:

a = []; b = []; a.push(b); b.push(a)
# Raises JSON::NestingError (nesting of 100 is too deep):
JSON.generate(a)
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 275

def generate(obj, opts = nil)
  if State === opts
    state, opts = opts, nil
  else
    state = SAFE_STATE_PROTOTYPE.dup
  end
  if opts
    if opts.respond_to? :to_hash
      opts = opts.to_hash
    elsif opts.respond_to? :to_h
      opts = opts.to_h
    else
      raise TypeError, "can't convert #{opts.class} into Hash"
    end
    state = state.configure(opts)
  end
  state.generate(obj)
end

.iconv(to, from, string)

Encodes string using String.encode.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 632

def self.iconv(to, from, string)
  string.encode(to, from)
end

.load(source, proc = nil, options = {}) ⇒ Object (mod_func) Also known as: .restore

Returns the Ruby objects created by parsing the given source.

  • Argument source must be, or be convertible to, a String:

    • If source responds to instance method to_str, source.to_str becomes the source.

    • If source responds to instance method to_io, source.to_io.read becomes the source.

    • If source responds to instance method read, source.read becomes the source.

    • If both of the following are true, source becomes the String 'null':

      • Option allow_blank specifies a truthy value.

      • The source, as defined above, is nil or the empty String ''.

    • Otherwise, source remains the source.

  • Argument proc, if given, must be a Proc that accepts one argument. It will be called recursively with each result (depth-first order). See details below. BEWARE: This method is meant to serialise data from trusted user input, like from your own database server or clients under your control, it could be dangerous to allow untrusted users to pass JSON sources into it.

  • Argument opts, if given, contains a Hash of options for the parsing. See Parsing Options. The default options can be changed via method JSON.load_default_options=.


When no proc is given, modifies source as above and returns the result of parse(source, opts); see #parse.

Source for following examples:

source = <<-EOT
{
"name": "Dave",
  "age" :40,
  "hats": [
    "Cattleman's",
    "Panama",
    "Tophat"
  ]
}
EOT

Load a String:

ruby = JSON.load(source)
ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

Load an IO object:

require 'stringio'
object = JSON.load(StringIO.new(source))
object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

Load a File object:

path = 't.json'
File.write(path, source)
File.open(path) do |file|
  JSON.load(file)
end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

When proc is given:

  • Modifies source as above.

  • Gets the result from calling parse(source, opts).

  • Recursively calls proc(result).

  • Returns the final result.

Example:

require 'json'

# Some classes for the example.
class Base
  def initialize(attributes)
    @attributes = attributes
  end
end
class User    < Base; end
class Account < Base; end
class Admin   < Base; end
# The JSON source.
json = <<-EOF
{
  "users": [
      {"type": "User", "username": "jane", "email": "jane@example.com"},
      {"type": "User", "username": "john", "email": "john@example.com"}
  ],
  "accounts": [
      {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
      {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
  ],
  "admins": {"type": "Admin", "password": "0wn3d"}
}
EOF
# Deserializer method.
def deserialize_obj(obj, safe_types = %w(User Account Admin))
  type = obj.is_a?(Hash) && obj["type"]
  safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
end
# Call to JSON.load
ruby = JSON.load(json, proc {|obj|
  case obj
  when Hash
    obj.each {|k, v| obj[k] = deserialize_obj v }
  when Array
    obj.map! {|v| deserialize_obj v }
  end
})
pp ruby

Output:

{"users"=>
   [#<User:0x00000000064c4c98
     @attributes=
       {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
     #<User:0x00000000064c4bd0
     @attributes=
       {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
 "accounts"=>
   [{"account"=>
       #<Account:0x00000000064c4928
       @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
    {"account"=>
       #<Account:0x00000000064c4680
       @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
 "admins"=>
   #<Admin:0x00000000064c41f8
   @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 536

def load(source, proc = nil, options = {})
  opts = load_default_options.merge options
  if source.respond_to? :to_str
    source = source.to_str
  elsif source.respond_to? :to_io
    source = source.to_io.read
  elsif source.respond_to?(:read)
    source = source.read
  end
  if opts[:allow_blank] && (source.nil? || source.empty?)
    source = 'null'
  end
  result = parse(source, opts)
  recurse_proc(result, &proc) if proc
  result
end

.load_file(path, opts = {}) ⇒ Object (mod_func)

Calls:

parse(File.read(path), opts)

See method #parse.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 224

def load_file(filespec, opts = {})
  parse(File.read(filespec), opts)
end

.load_file!(path, opts = {}) (mod_func)

Calls:

JSON.parse!(File.read(path, opts))

See method #parse!

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 235

def load_file!(filespec, opts = {})
  parse!(File.read(filespec), opts)
end

.parse(source, opts) ⇒ Object (mod_func)

Returns the Ruby objects created by parsing the given source.

Argument source contains the String to be parsed.

Argument opts, if given, contains a Hash of options for the parsing. See Parsing Options.


When source is a JSON array, returns a Ruby Array:

source = '["foo", 1.0, true, false, null]'
ruby = JSON.parse(source)
ruby # => ["foo", 1.0, true, false, nil]
ruby.class # => Array

When source is a JSON object, returns a Ruby Hash:

source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
ruby = JSON.parse(source)
ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
ruby.class # => Hash

For examples of parsing for all JSON data types, see Parsing JSON.

Parses nested JSON objects:

source = <<-EOT
{
"name": "Dave",
  "age" :40,
  "hats": [
    "Cattleman's",
    "Panama",
    "Tophat"
  ]
}
EOT
ruby = JSON.parse(source)
ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}

Raises an exception if source is not valid JSON:

# Raises JSON::ParserError (783: unexpected token at ''):
JSON.parse('')
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 194

def parse(source, opts = {})
  Parser.new(source, **(opts||{})).parse
end

.parse!(source, opts) ⇒ Object (mod_func)

Calls

parse(source, opts)

with source and possibly modified opts.

Differences from .parse:

  • Option max_nesting, if not provided, defaults to false, which disables checking for nesting depth.

  • Option allow_nan, if not provided, defaults to true.

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 209

def parse!(source, opts = {})
  opts = {
    :max_nesting  => false,
    :allow_nan    => true
  }.merge(opts)
  Parser.new(source, **(opts||{})).parse
end

.pretty_generate(obj, opts = nil) ⇒ String (mod_func) Also known as: .pretty_unparse

Arguments obj and opts here are the same as arguments obj and opts in .generate.

Default options are:

{
  indent: '  ',   # Two spaces
  space: ' ',     # One space
  array_nl: "\n", # Newline
  object_nl: "\n" # Newline
}

Example:

obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
json = JSON.pretty_generate(obj)
puts json

Output:

{
  "foo": [
    "bar",
    "baz"
  ],
  "bat": {
    "bam": 0,
    "bad": 1
  }
}
[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 369

def pretty_generate(obj, opts = nil)
  if State === opts
    state, opts = opts, nil
  else
    state = PRETTY_STATE_PROTOTYPE.dup
  end
  if opts
    if opts.respond_to? :to_hash
      opts = opts.to_hash
    elsif opts.respond_to? :to_h
      opts = opts.to_h
    else
      raise TypeError, "can't convert #{opts.class} into Hash"
    end
    state.configure(opts)
  end
  state.generate(obj)
end

.pretty_unparse (mod_func)

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 390

alias pretty_unparse pretty_generate

.recurse_proc(result, &proc) (mod_func)

This method is for internal use only.

Recursively calls passed Proc if the parsed data structure is an Array or Hash

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 554

def recurse_proc(result, &proc) # :nodoc:
  case result
  when Array
    result.each { |x| recurse_proc x, &proc }
    proc.call result
  when Hash
    result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
    proc.call result
  else
    proc.call result
  end
end

.restore (mod_func)

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 567

alias restore load

.unparse (mod_func)

[ GitHub ]

  
# File 'ext/json/lib/json/common.rb', line 297

alias unparse generate