module JSON

JavaScript Object Notation (JSON)

JSON is a lightweight data-interchange format.

JSON is easy for us humans to read and write, and equally simple for machines to read (parse) and write (generate).

JSON is language-independent, making it an ideal interchange format for applications in differing programming languages and on differing operating systems.

JSON Values

A JSON value is one of the following:

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:

where

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; You can set it to false to disable depth checking entirely, but that is dangerous when parsing untrusted input.

With the default, 100:

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_duplicate_key specifies whether duplicate keys in objects should be ignored or cause an error to be raised:

When set to false, the default:

JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError)

When set to true:

# The last value is used.
JSON.parse('{"a": 1, "a":2}', allow_duplicate_key: true) => {"a" => 2}

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]

Option allow_trailing_comma (boolean) specifies whether to allow trailing commas in objects and arrays; defaults to false.

With the default, false:

JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError)

When enabled:

JSON.parse('[1,]', allow_trailing_comma: true) # => [1]

Option allow_comments (boolean) specifies whether to allow JavaScript style comments (either // comment or /* comment */); defaults to false.

When set to false, the default:

JSON.parse('/* comment */ {"a": 1, "a":2}') # unexpected character: '/' at line 1 column 1 (JSON::ParserError)

When set to true, comments are ignored:

JSON.parse('/* comment */ {"a": 1, "a":2} // more comment') # => {"a" => 2}

Option allow_control_characters (boolean) specifies whether to allow unescaped ASCII control characters, such as newlines, in strings; defaults to false.

With the default, false:

JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)

When enabled:

JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"

Option allow_invalid_escape (boolean) specifies whether to ignore backslahes that are followed by an invalid escape character in strings; defaults to false.

With the default, false:

JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)

When enabled:

JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
Output Options

Option freeze (boolean) specifies whether the returned objects will be frozen; defaults to false.

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}>

Generating JSON

To generate a Ruby String containing JSON data, use method JSON.generate(source, opts), where

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, JSON::NaN, JSON::Infinity, JSON::MinusInfinity]
JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,NaN,Infinity,-Infinity]'

Option allow_duplicate_key (boolean) specifies whether hashes with duplicate keys should be allowed or produce an error. defaults to emit a deprecation warning.

With the default, false:

JSON.generate({ foo: 1, "foo" => 2 })
# detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)

With true

JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: true)
# => '{"foo":1,"foo":2}'

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)

With false:

obj = []
obj[0] = obj
# Raises  SystemStackError: stack level too deep
JSON.generate(obj, max_nesting: false)

Setting max_nesting to false or a very large number can lead to a stack overflow which may leave the process in an unrecoverable state. It is highly discouraged.

Escaping Options

Options script_safe (boolean) specifies wether '\u2028', '\u2029' and '/' should be escaped as to make the JSON object safe to interpolate in script tags.

Options ascii_only (boolean) specifies wether all characters outside the ASCII range should be escaped.

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.

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
  }
}

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]

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_duplicate_key specifies whether duplicate keys in objects should be ignored or cause an error to be raised:

When not specified: # The last value is used and a deprecation warning emitted. JSON.parse(β€˜{β€œa”: 1, β€œa”:2}’) => {β€œa” => 2} # warning: detected duplicate keys in JSON object. # This will raise an error in json 3.0 unless enabled via allow_duplicate_key: true

When set to true # The last value is used. JSON.parse(β€˜{β€œa”: 1, β€œa”:2}’) => {β€œa” => 2}

When set to false, the future default: JSON.parse(β€˜{β€œa”: 1, β€œa”:2}’) => duplicate key at line 1 column 1 (JSON::ParserError)


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]


Option allow_trailing_comma (boolean) specifies whether to allow trailing commas in objects and arrays; defaults to false.

With the default, false: JSON.parse(β€˜[1,]’) # unexpected character: β€˜]’ at line 1 column 4 (JSON::ParserError)

When enabled: JSON.parse(β€˜[1,]’, allow_trailing_comma: true) # => [1]

Output Options

Option freeze (boolean) specifies whether the returned objects will be frozen; defaults to false.

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 allow_duplicate_key (boolean) specifies whether hashes with duplicate keys should be allowed or produce an error. defaults to emit a deprecation warning.

With the default, (not set): Warning = true JSON.generate({ foo: 1, β€œfoo” => 2 }) # warning: detected duplicate key β€œfoo” in {foo: 1, β€œfoo” => 2}. # This will raise an error in json 3.0 unless enabled via allow_duplicate_key: true # => β€˜{β€œfoo”:1,β€œfoo”:2}’

With false JSON.generate({ foo: 1, β€œfoo” => 2 }, allow_duplicate_key: false) # detected duplicate key β€œfoo” in {foo: 1, β€œfoo” => 2} (JSON::GeneratorError)

In version 3.0, false will become the default.


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)

Escaping Options

Options script_safe (boolean) specifies wether '\u2028', '\u2029' and '/' should be escaped as to make the JSON object safe to interpolate in script tags.

Options ascii_only (boolean) specifies wether all characters outside the ASCII range should be escaped.

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.

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

Note that JSON Additions must only be used with trusted data, and is deprecated.

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) 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)