JavaScript Object Notation (JSON)
JSON is a lightweight data-interchange format.
A JSON value is one of the following:
-
Double-quoted text:
"foo"
. -
Number:
1
,1.0
,2.0e2
. -
Boolean:
true
,false
. -
Null:
null
. -
Array: an ordered list of values, enclosed by square brackets:
["foo", 1, 1.0, 2.0e2, true, false, null]
-
Object: a collection of name/value pairs, enclosed by curly braces; each name is double-quoted text; the values may be any JSON values:
{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
A JSON array or object may contain nested arrays, objects, and scalars to any depth:
{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} [{"foo": 0, "bar": 1}, ["baz", 2]]
Using Module JSON
To make module JSON available in your code, begin with:
require 'json'
All examples here assume that this has been done.
Parsing JSON
You can parse a String containing JSON data using either of two methods:
-
JSON.parse(source, opts)
-
JSON.parse!(source, opts)
where
-
source
is a Ruby object. -
opts
is a Hash object containing options that control both input allowed and output formatting.
The difference between the two methods is that JSON.parse!
omits some checks and may not be safe for some source
data; use it only for data from trusted sources. Use the safer method JSON.parse
for less trusted sources.
Parsing JSON Arrays
When source
is a JSON array, JSON.parse
by default returns a Ruby Array:
json = '["foo", 1, 1.0, 2.0e2, true, false, null]' ruby = JSON.parse(json) ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] ruby.class # => Array
The JSON array may contain nested arrays, objects, and scalars to any depth:
json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
Parsing JSON Objects
When the source is a JSON object, JSON.parse
by default returns a Ruby Hash:
json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' ruby = JSON.parse(json) ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} ruby.class # => Hash
The JSON object may contain nested arrays, objects, and scalars to any depth:
json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
Parsing JSON Scalars
When the source is a JSON scalar (not an array or object), JSON.parse
returns a Ruby scalar.
String:
ruby = JSON.parse('"foo"') ruby # => 'foo' ruby.class # => String
Integer:
ruby = JSON.parse('1') ruby # => 1 ruby.class # => Integer
Float:
ruby = JSON.parse('1.0') ruby # => 1.0 ruby.class # => Float ruby = JSON.parse('2.0e2') ruby # => 200 ruby.class # => Float
Boolean:
ruby = JSON.parse('true') ruby # => true ruby.class # => TrueClass ruby = JSON.parse('false') ruby # => false ruby.class # => FalseClass
Null:
ruby = JSON.parse('null') ruby # => nil ruby.class # => NilClass
Parsing Options
Input Options
Option max_nesting
(Integer) specifies the maximum nesting depth allowed; defaults to 100
; specify false
to disable depth checking.
With the default, false
:
source = '[0, [1, [2, [3]]]]' ruby = JSON.parse(source) ruby # => [0, [1, [2, [3]]]]
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.parse(source, {max_nesting: 1})
Bad value:
# Raises TypeError (wrong argument type Symbol (expected Fixnum)): JSON.parse(source, {max_nesting: :foo})
Option allow_nan
(boolean) specifies whether to allow NaN
, Infinity
, and MinusInfinity
in source
; defaults to false
.
With the default, false
:
# Raises JSON::ParserError (225: unexpected token at '[NaN]'): JSON.parse('[NaN]') # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): JSON.parse('[Infinity]') # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): JSON.parse('[-Infinity]')
Allow:
source = '[NaN, Infinity, -Infinity]' ruby = JSON.parse(source, {allow_nan: true}) ruby # => [NaN, Infinity, -Infinity]
Output Options
Option symbolize_names
(boolean) specifies whether returned Hash keys should be Symbols; defaults to false
(use Strings).
With the default, false
:
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}
Use Symbols:
ruby = JSON.parse(source, {symbolize_names: true}) ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
Option object_class
(Class) specifies the Ruby class to be used for each JSON object; defaults to Hash.
With the default, Hash:
source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' ruby = JSON.parse(source) ruby.class # => Hash
Use class OpenStruct:
ruby = JSON.parse(source, {object_class: OpenStruct}) ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
Option array_class
(Class) specifies the Ruby class to be used for each JSON array; defaults to Array.
With the default, Array:
source = '["foo", 1.0, true, false, null]' ruby = JSON.parse(source) ruby.class # => Array
Use class Set:
ruby = JSON.parse(source, {array_class: Set}) ruby # => #<Set: {"foo", 1.0, true, false, nil}>
Option create_additions
(boolean) specifies whether to use JSON additions in parsing. See JSON Additions.
Generating JSON
To generate a Ruby String containing JSON data, use method JSON.generate(source, opts)
, where
-
source
is a Ruby object. -
opts
is a Hash object containing options that control both input allowed and output formatting.
Generating JSON from Arrays
When the source is a Ruby Array, JSON.generate
returns a String containing a JSON array:
ruby = [0, 's', :foo] json = JSON.generate(ruby) json # => '[0,"s","foo"]'
The Ruby Array array may contain nested arrays, hashes, and scalars to any depth:
ruby = [0, [1, 2], {foo: 3, bar: 4}] json = JSON.generate(ruby) json # => '[0,[1,2],{"foo":3,"bar":4}]'
Generating JSON from Hashes
When the source is a Ruby Hash, JSON.generate
returns a String containing a JSON object:
ruby = {foo: 0, bar: 's', baz: :bat} json = JSON.generate(ruby) json # => '{"foo":0,"bar":"s","baz":"bat"}'
The Ruby Hash array may contain nested arrays, hashes, and scalars to any depth:
ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} json = JSON.generate(ruby) json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
Generating JSON from Other Objects
When the source is neither an Array nor a Hash, the generated JSON data depends on the class of the source.
When the source is a Ruby Integer or Float, JSON.generate
returns a String containing a JSON number:
JSON.generate(42) # => '42' JSON.generate(0.42) # => '0.42'
When the source is a Ruby String, JSON.generate
returns a String containing a JSON string (with double-quotes):
JSON.generate('A string') # => '"A string"'
When the source is true
, false
or nil
, JSON.generate
returns a String containing the corresponding JSON token:
JSON.generate(true) # => 'true' JSON.generate(false) # => 'false' JSON.generate(nil) # => 'null'
When the source is none of the above, JSON.generate
returns a String containing a JSON string representation of the source:
JSON.generate(:foo) # => '"foo"' JSON.generate(Complex(0, 0)) # => '"0+0i"' JSON.generate(Dir.new('.')) # => '"#<Dir>"'
Generating Options
Input Options
Option allow_nan
(boolean) specifies whether NaN
, Infinity
, and -Infinity
may be generated; defaults to false
.
With the default, false
:
# Raises JSON::GeneratorError (920: NaN not allowed in JSON): JSON.generate(JSON::NaN) # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): JSON.generate(JSON::Infinity) # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): JSON.generate(JSON::MinusInfinity)
Allow:
ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
Option max_nesting
(Integer) specifies the maximum nesting depth in obj
; defaults to 100
.
With the default, 100
:
obj = [[[[[[0]]]]]] JSON.generate(obj) # => '[[[[[[0]]]]]]'
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.generate(obj, max_nesting: 2)
Output Options
The default formatting options generate the most compact JSON data, all on one line and with no whitespace.
You can use these formatting options to generate JSON data in a more open format, using whitespace. See also JSON.pretty_generate
.
-
Option
array_nl
(String) specifies a string (usually a newline) to be inserted after each JSON array; defaults to the empty String,''
. -
Option
object_nl
(String) specifies a string (usually a newline) to be inserted after each JSON object; defaults to the empty String,''
. -
Option
indent
(String) specifies the string (usually spaces) to be used for indentation; defaults to the empty String,''
; defaults to the empty String,''
; has no effect unless optionsarray_nl
orobject_nl
specify newlines. -
Option
space
(String) specifies a string (usually a space) to be inserted after the colon in each JSON object’s pair; defaults to the empty String,''
. -
Option
space_before
(String) specifies a string (usually a space) to be inserted before the colon in each JSON object’s pair; defaults to the empty String,''
.
In this example, obj
is used first to generate the shortest JSON data (no whitespace), then again with all formatting options specified:
obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} json = JSON.generate(obj) puts 'Compact:', json opts = { array_nl: "\n", object_nl: "\n", indent: ' ', space_before: ' ', space: ' ' } puts 'Open:', JSON.generate(obj, opts)
Output:
Compact: {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} Open: { "foo" : [ "bar", "baz" ], "bat" : { "bam" : 0, "bad" : 1 } }
JSON Additions
When you “round trip” a non-String object from Ruby to JSON and back, you have a new String, instead of the object you began with:
ruby0 = Range.new(0, 2) json = JSON.generate(ruby0) json # => '0..2"' ruby1 = JSON.parse(json) ruby1 # => '0..2' ruby1.class # => String
You can use JSON additions to preserve the original object. The addition is an extension of a ruby class, so that:
-
JSON.generate stores more information in the JSON string.
-
JSON.parse, called with option
create_additions
, uses that information to create a proper Ruby object.
This example shows a Range being generated into JSON and parsed back into Ruby, both without and with the addition for Range:
ruby = Range.new(0, 2) # This passage does not use the addition for Range. json0 = JSON.generate(ruby) ruby0 = JSON.parse(json0) # This passage uses the addition for Range. require 'json/add/range' json1 = JSON.generate(ruby) ruby1 = JSON.parse(json1, create_additions: true) # Make a nice display. display = <<EOT Generated JSON: Without addition: #{json0} (#{json0.class}) With addition: #{json1} (#{json1.class}) Parsed JSON: Without addition: #{ruby0.inspect} (#{ruby0.class}) With addition: #{ruby1.inspect} (#{ruby1.class}) EOT puts display
This output shows the different results:
Generated JSON: Without addition: "0..2" (String) With addition: {"json_class":"Range","a":[0,2,false]} (String) Parsed JSON: Without addition: "0..2" (String) With addition: 0..2 (Range)
The JSON module includes additions for certain classes. You can also craft custom additions. See Custom JSON Additions.
Built-in Additions
The JSON module includes additions for certain classes. To use an addition, require
its source:
-
BigDecimal:
require 'json/add/bigdecimal'
-
Complex:
require 'json/add/complex'
-
Date:
require 'json/add/date'
-
DateTime:
require 'json/add/date_time'
-
Exception:
require 'json/add/exception'
-
OpenStruct:
require 'json/add/ostruct'
-
Range:
require 'json/add/range'
-
Rational:
require 'json/add/rational'
-
Regexp:
require 'json/add/regexp'
-
Set:
require 'json/add/set'
-
Struct:
require 'json/add/struct'
-
Symbol:
require 'json/add/symbol'
-
Time:
require 'json/add/time'
To reduce punctuation clutter, the examples below show the generated JSON via puts
, rather than the usual inspect
,
BigDecimal:
require 'json/add/bigdecimal' ruby0 = BigDecimal(0) # 0.0 json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} ruby1 = JSON.parse(json, create_additions: true) # 0.0 ruby1.class # => BigDecimal
Complex:
require 'json/add/complex' ruby0 = Complex(1+0i) # 1+0i json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} ruby1 = JSON.parse(json, create_additions: true) # 1+0i ruby1.class # Complex
Date:
require 'json/add/date' ruby0 = Date.today # 2020-05-02 json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 ruby1.class # Date
DateTime:
require 'json/add/date_time' ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 ruby1.class # DateTime
Exception (and its subclasses including RuntimeError):
require 'json/add/exception' ruby0 = Exception.new('A message') # A message json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} ruby1 = JSON.parse(json, create_additions: true) # A message ruby1.class # Exception ruby0 = RuntimeError.new('Another message') # Another message json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} ruby1 = JSON.parse(json, create_additions: true) # Another message ruby1.class # RuntimeError
OpenStruct:
require 'json/add/ostruct' ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby"> json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby"> ruby1.class # OpenStruct
Range:
require 'json/add/range' ruby0 = Range.new(0, 2) # 0..2 json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} ruby1 = JSON.parse(json, create_additions: true) # 0..2 ruby1.class # Range
Rational:
require 'json/add/rational' ruby0 = Rational(1, 3) # 1/3 json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} ruby1 = JSON.parse(json, create_additions: true) # 1/3 ruby1.class # Rational
Regexp:
require 'json/add/regexp' ruby0 = Regexp.new('foo') # (?-mix:foo) json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) ruby1.class # Regexp
Set:
require 'json/add/set' ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}> json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}> ruby1.class # Set
Struct:
require 'json/add/struct' Customer = Struct.new(:name, :address) # Customer ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main"> json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main"> ruby1.class # Customer
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)
For backwards compatibility
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
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
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
Sets or returns default options for the JSON.load
method. Initially:
opts = JSON.load_default_options opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
Sets or returns the default options for the JSON.dump
method. Initially:
opts = JSON.dump_default_options opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
# File tmp/rubies/ruby-3.1.3/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
If object
is a String, calls JSON.parse
with object
and opts
(see method parse
):
json = '[0, 1, null]' JSON[json]# => [0, 1, nil]
Otherwise, calls JSON.generate
with object
and opts
(see method generate
):
ruby = [0, 1, nil] JSON[ruby] # => '[0,1,null]'
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 81
def create_fast_state
State.new(
:indent => '',
:space => '',
:object_nl => "",
:array_nl => "",
:max_nesting => false
)
end
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 126
def self.create_id
Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
end
Returns the current create identifier. See also JSON.create_id=
.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 120
def self.create_id=(new_value)
Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
end
Sets 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'
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 91
def create_pretty_state
State.new(
:indent => ' ',
:space => ' ',
:object_nl => "\n",
:array_nl => "\n"
)
end
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 653
def self.iconv(to, from, string)
string.encode(to, from)
end
Encodes string using String.encode
.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 631
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
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 JSON.dump_default_options
.
-
Argument
io
, if given, should respond to methodwrite
; the JSON String is written toio
, andio
is returned. Ifio
is not given, the JSON String is returned. -
Argument
limit
, if given, is passed toJSON.generate
as optionmax_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"}
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 335
def fast_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = JSON.create_fast_state
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
Arguments obj
and opts
here are the same as arguments obj
and opts
in JSON.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)
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 296
def generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = State.new
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
Returns a String containing the generated JSON data.
See also JSON.fast_generate
, JSON.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)
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 557
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
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 methodto_str
,source.to_str
becomes the source. -
If
source
responds to instance methodto_io
,source.to_io.read
becomes the source. -
If
source
responds to instance methodread
,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 passJSON
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 methodJSON.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 callingparse(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"}>}
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 245
def load_file(filespec, opts = {})
parse(File.read(filespec), opts)
end
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 256
def load_file!(filespec, opts = {})
parse!(File.read(filespec), opts)
end
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 215
def parse(source, opts = {})
Parser.new(source, **(opts||{})).parse
end
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('')
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 230
def parse!(source, opts = {})
opts = {
:max_nesting => false,
:allow_nan => true
}.merge(opts)
Parser.new(source, **(opts||{})).parse
end
Calls
parse(source, opts)
with source
and possibly modified opts
.
Differences from JSON.parse
:
-
Option
max_nesting
, if not provided, defaults tofalse
, which disables checking for nesting depth. -
Option
allow_nan
, if not provided, defaults totrue
.
# File tmp/rubies/ruby-3.1.3/ext/json/lib/json/common.rb, line 390
def pretty_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = JSON.create_pretty_state
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
Arguments obj
and opts
here are the same as arguments obj
and opts
in JSON.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 } }