Module: JSON
- Defined in:
- lib/json.rb,
lib/json/ext.rb,
lib/json/common.rb,
lib/json/version.rb,
lib/json/generic_object.rb,
parser/parser.c,
generator/generator.c
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(, baz)
self. =
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' => [ , 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)
Defined Under Namespace
Modules: Ext Classes: CircularDatastructure, GeneratorError, GenericObject, JSONError, MissingUnicodeSupport, NestingError, ParserError
Constant Summary collapse
- JSON_LOADED =
true
- NaN =
0.0/0
- Infinity =
1.0/0
- MinusInfinity =
-Infinity
- UnparserError =
For backwards compatibility
GeneratorError
- VERSION =
JSON version
'2.5.1'
- VERSION_ARRAY =
:nodoc:
VERSION.split(/\./).map { |x| x.to_i }
- VERSION_MAJOR =
:nodoc:
VERSION_ARRAY[0]
- VERSION_MINOR =
:nodoc:
VERSION_ARRAY[1]
- VERSION_BUILD =
:nodoc:
VERSION_ARRAY[2]
Class Attribute Summary collapse
-
.dump_default_options ⇒ Object
Sets or returns the default options for the JSON.dump method.
-
.generator ⇒ Object
Returns the JSON generator module that is used by JSON.
-
.load_default_options ⇒ Object
Sets or returns default options for the JSON.load method.
-
.parser ⇒ Object
Returns the JSON parser class that is used by JSON.
-
.state ⇒ Object
Sets or Returns the JSON generator state class that is used by JSON.
Class Method Summary collapse
-
.[](object, opts = {}) ⇒ Object
:call-seq: JSON -> new_array or new_string.
- .create_fast_state ⇒ Object
-
.create_id ⇒ Object
Returns the current create identifier.
-
.create_id=(new_value) ⇒ Object
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’. - .create_pretty_state ⇒ Object
-
.deep_const_get(path) ⇒ Object
Return the constant located at path.
-
.dump(obj, anIO = nil, limit = nil) ⇒ Object
:call-seq: JSON.dump(obj, io = nil, limit = nil).
-
.fast_generate(obj, opts = nil) ⇒ Object
(also: fast_unparse)
:call-seq: JSON.fast_generate(obj, opts) -> new_string.
-
.generate(obj, opts = nil) ⇒ Object
(also: unparse)
:call-seq: JSON.generate(obj, opts = nil) -> new_string.
-
.iconv(to, from, string) ⇒ Object
Encodes string using String.encode.
-
.load(source, proc = nil, options = {}) ⇒ Object
(also: restore)
:call-seq: JSON.load(source, proc = nil, options = {}) -> object.
-
.load_file(filespec, opts = {}) ⇒ Object
:call-seq: JSON.load_file(path, opts={}) -> object.
-
.load_file!(filespec, opts = {}) ⇒ Object
:call-seq: JSON.load_file!(path, opts = {}).
-
.parse(source, opts = {}) ⇒ Object
:call-seq: JSON.parse(source, opts) -> object.
-
.parse!(source, opts = {}) ⇒ Object
:call-seq: JSON.parse!(source, opts) -> object.
-
.pretty_generate(obj, opts = nil) ⇒ Object
(also: pretty_unparse)
:call-seq: JSON.pretty_generate(obj, opts = nil) -> new_string.
-
.recurse_proc(result, &proc) ⇒ Object
Recursively calls passed Proc if the parsed data structure is an Array or Hash.
Class Attribute Details
.dump_default_options ⇒ Object
Sets or returns the default options for the JSON.dump method. Initially:
opts = JSON.
opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
596 597 598 |
# File 'lib/json/common.rb', line 596 def @dump_default_options end |
.generator ⇒ Object
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
103 104 105 |
# File 'lib/json/common.rb', line 103 def generator @generator end |
.load_default_options ⇒ Object
Sets or returns default options for the JSON.load method. Initially:
opts = JSON.
opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
420 421 422 |
# File 'lib/json/common.rb', line 420 def @load_default_options end |
.parser ⇒ Object
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
29 30 31 |
# File 'lib/json/common.rb', line 29 def parser @parser end |
.state ⇒ Object
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
108 109 110 |
# File 'lib/json/common.rb', line 108 def state @state end |
Class Method Details
.[](object, opts = {}) ⇒ Object
:call-seq:
JSON[object] -> new_array or new_string
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]'
18 19 20 21 22 23 24 |
# File '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 |
.create_fast_state ⇒ Object
81 82 83 84 85 86 87 88 89 |
# File 'lib/json/common.rb', line 81 def create_fast_state State.new( :indent => '', :space => '', :object_nl => "", :array_nl => "", :max_nesting => false ) end |
.create_id ⇒ Object
Returns the current create identifier. See also JSON.create_id=.
126 127 128 |
# File 'lib/json/common.rb', line 126 def self.create_id Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID end |
.create_id=(new_value) ⇒ Object
120 121 122 |
# File 'lib/json/common.rb', line 120 def self.create_id=(new_value) Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze end |
.create_pretty_state ⇒ Object
91 92 93 94 95 96 97 98 |
# File 'lib/json/common.rb', line 91 def create_pretty_state State.new( :indent => ' ', :space => ' ', :object_nl => "\n", :array_nl => "\n" ) end |
.deep_const_get(path) ⇒ Object
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.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File '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, anIO = nil, limit = nil) ⇒ Object
:call-seq:
JSON.dump(obj, io = nil, limit = nil)
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 to JSON.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"}
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File '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. 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 = nil) ⇒ Object Also known as: fast_unparse
:call-seq:
JSON.fast_generate(obj, opts) -> new_string
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)
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File '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 |
.generate(obj, opts = nil) ⇒ Object Also known as: unparse
:call-seq:
JSON.generate(obj, opts = nil) -> new_string
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)
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File '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 |
.iconv(to, from, string) ⇒ Object
Encodes string using String.encode.
653 654 655 |
# File 'lib/json/common.rb', line 653 def self.iconv(to, from, string) string.encode(to, from) end |
.load(source, proc = nil, options = {}) ⇒ Object Also known as: restore
:call-seq:
JSON.load(source, proc = nil, options = {}) -> object
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 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 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": "[email protected]"},
{"type": "User", "username": "john", "email": "[email protected]"}
],
"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"=>"[email protected]"}>,
#<User:0x00000000064c4bd0
@attributes=
{"type"=>"User", "username"=>"john", "email"=>"[email protected]"}>],
"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"}>}
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 |
# File 'lib/json/common.rb', line 557 def load(source, proc = nil, = {}) opts = .merge 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(filespec, opts = {}) ⇒ Object
:call-seq:
JSON.load_file(path, opts={}) -> object
Calls:
parse(File.read(path), opts)
See method #parse.
245 246 247 |
# File 'lib/json/common.rb', line 245 def load_file(filespec, opts = {}) parse(File.read(filespec), opts) end |
.load_file!(filespec, opts = {}) ⇒ Object
256 257 258 |
# File 'lib/json/common.rb', line 256 def load_file!(filespec, opts = {}) parse!(File.read(filespec), opts) end |
.parse(source, opts = {}) ⇒ Object
:call-seq:
JSON.parse(source, opts) -> object
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('')
215 216 217 |
# File 'lib/json/common.rb', line 215 def parse(source, opts = {}) Parser.new(source, **(opts||{})).parse end |
.parse!(source, opts = {}) ⇒ Object
:call-seq:
JSON.parse!(source, opts) -> object
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
.
230 231 232 233 234 235 236 |
# File '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 |
.pretty_generate(obj, opts = nil) ⇒ Object Also known as: pretty_unparse
:call-seq:
JSON.pretty_generate(obj, opts = nil) -> new_string
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
}
}
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File '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 |
.recurse_proc(result, &proc) ⇒ Object
Recursively calls passed Proc if the parsed data structure is an Array or Hash
575 576 577 578 579 580 581 582 583 584 585 586 |
# File 'lib/json/common.rb', line 575 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 |