summaryrefslogtreecommitdiff
path: root/ext/json/lib
diff options
context:
space:
mode:
authorBurdetteLamar <burdettelamar@yahoo.com>2020-05-06 07:59:54 -0500
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2020-07-01 18:47:50 +0900
commit4689fd5f9982a148052d76e8e1a7bd85256baec6 (patch)
tree3c329cf9bc2375134bb256e8ffdd28a51427e434 /ext/json/lib
parent1351374bd14d4d134726889ba195609b4f745ea6 (diff)
[flori/json] Rdoc enhancements
https://github.com/flori/json/commit/e7e3732130
Diffstat (limited to 'ext/json/lib')
-rw-r--r--ext/json/lib/json.rb441
-rw-r--r--ext/json/lib/json/common.rb445
2 files changed, 732 insertions, 154 deletions
diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb
index 151d9c2f26..a96f4bc612 100644
--- a/ext/json/lib/json.rb
+++ b/ext/json/lib/json.rb
@@ -2,88 +2,405 @@
require 'json/common'
##
-# = JavaScript Object Notation (JSON)
+# = JavaScript \Object Notation (\JSON)
#
-# JSON is a lightweight data-interchange format. It is easy for us
-# humans to read and write. Plus, equally simple for machines to generate or parse.
-# JSON is completely language agnostic, making it the ideal interchange format.
+# \JSON is a lightweight data-interchange format.
#
-# Built on two universally available structures:
+# A \JSON value is one of the following:
+# - Double-quoted text: <tt>"foo"</tt>.
+# - 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]
#
-# 1. A collection of name/value pairs. Often referred to as an _object_, hash table,
-# record, struct, keyed list, or associative array.
-# 2. An ordered list of values. More commonly called an _array_, vector, sequence or
-# list.
+# - \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}
#
-# To read more about JSON visit: http://json.org
+# 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]]
#
-# == Parsing JSON
-#
-# To parse a JSON string received by another application or generated within
-# your existing application:
+# == Using \Module \JSON
#
+# To make module \JSON available in your code, begin with:
# require 'json'
#
-# my_hash = JSON.parse('{"hello": "goodbye"}')
-# puts my_hash["hello"] # => "goodbye"
+# All examples here assume that this has been done.
#
-# Notice the extra quotes <tt>''</tt> around the hash notation. Ruby expects
-# the argument to be a string and can't convert objects like a hash or array.
+# === Parsing \JSON
#
-# Ruby converts your string into a hash
+# You can parse a \String containing \JSON data using
+# either of two methods:
+# - <tt>JSON.parse(source, opts)</tt>
+# - <tt>JSON.parse!(source, opts)</tt>
#
-# == Generating JSON
+# where
+# - +source+ is a Ruby object.
+# - +opts+ is a \Hash object containing options
+# that control both input allowed and output formatting.
#
-# Creating a JSON string for communication or serialization is
-# just as simple.
+# 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.
#
-# require 'json'
+# ==== Parsing \JSON Arrays
#
-# my_hash = {:hello => "goodbye"}
-# puts JSON.generate(my_hash) # => "{\"hello\":\"goodbye\"}"
+# 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
#
-# Or an alternative way:
+# 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]]
#
-# require 'json'
-# puts({:hello => "goodbye"}.to_json) # => "{\"hello\":\"goodbye\"}"
+# ==== 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
+#
+# === Generating \JSON
+#
+# To generate a Ruby \String containing \JSON data,
+# use method <tt>JSON.generate(source, opts)</tt>, 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(Integer(0)) # => "0""
+# JSON.generate(Float(1.0)) # => "1.0"
+#
+# 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:0x0000000006bb30b8>\""
+#
+# == \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.
#
-# <tt>JSON.generate</tt> only allows objects or arrays to be converted
-# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
-# even though it acts only as a method for serialization:
+# 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}[#module-JSON-label-Custom+JSON+Additions].
+#
+# === Built-in Additions
+#
+# The \JSON module includes additions for certain classes.
+# To use an addition, +require+ its source:
+# - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
+# - Complex: <tt>require 'json/add/complex'</tt>
+# - Date: <tt>require 'json/add/date'</tt>
+# - DateTime: <tt>require 'json/add/date_time'</tt>
+# - Exception: <tt>require 'json/add/exception'</tt>
+# - OpenStruct: <tt>require 'json/add/ostruct'</tt>
+# - Range: <tt>require 'json/add/range'</tt>
+# - Rational: <tt>require 'json/add/rational'</tt>
+# - Regexp: <tt>require 'json/add/regexp'</tt>
+# - Set: <tt>require 'json/add/set'</tt>
+# - Struct: <tt>require 'json/add/struct'</tt>
+# - Symbol: <tt>require 'json/add/symbol'</tt>
+# - Time: <tt>require 'json/add/time'</tt>
+#
+# 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)
#
-# 1.to_json # => "1"
-#
-# The {#generate}[rdoc-ref:JSON#generate] method accepts a variety of options
-# to set the formatting of string output and defining what input is accepteable.
-# There are also shortcut methods pretty_generate (with a set of options to
-# generate human-readable multiline JSON) and fast_generate (with a set of
-# options to generate JSON faster at the price of disabling some checks).
-#
-# == Extended rendering and loading of Ruby objects
-#
-# JSON library provides optional _additions_ allowing to serialize and
-# deserialize Ruby classes without loosing their type.
-#
-# # without additions
-# require "json"
-# json = JSON.generate({range: 1..3, regex: /test/})
-# # => '{"range":"1..3","regex":"(?-mix:test)"}'
-# JSON.parse(json)
-# # => {"range"=>"1..3", "regex"=>"(?-mix:test)"}
-#
-# # with additions
-# require "json/add/range"
-# require "json/add/regexp"
-# json = JSON.generate({range: 1..3, regex: /test/})
-# # => '{"range":{"json_class":"Range","a":[1,3,false]},"regex":{"json_class":"Regexp","o":0,"s":"test"}}'
-# JSON.parse(json)
-# # => {"range"=>{"json_class"=>"Range", "a"=>[1, 3, false]}, "regex"=>{"json_class"=>"Regexp", "o"=>0, "s"=>"test"}}
-# JSON.load(json)
-# # => {"range"=>1..3, "regex"=>/test/}
-#
-# See JSON.load for details.
module JSON
require 'json/version'
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index 5ba8f1d298..0e69050b17 100644
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
@@ -4,12 +4,15 @@ require 'json/generic_object'
module JSON
class << self
- # If _object_ is string-like, parse the string and return the parsed
- # result as a Ruby data structure. Otherwise generate a JSON text from the
- # Ruby data structure object and return it.
+ # If +object+ is a
+ # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
+ # (implementing +to_str+), calls JSON.parse with +object+ and +opts+:
+ # json = '[0, 1, null]'
+ # JSON[json]# => [0, 1, nil]
#
- # The _opts_ argument is passed through to generate/parse respectively.
- # See generate and parse for their documentation.
+ # Otherwise, calls JSON.generate with +object+ and +opts+:
+ # ruby = [0, 1, nil]
+ # JSON[ruby] # => "[0,1,null]"
def [](object, opts = {})
if object.respond_to? :to_str
JSON.parse(object.to_str, opts)
@@ -19,7 +22,8 @@ module JSON
end
# Returns the JSON parser class that is used by JSON. This is either
- # JSON::Ext::Parser or JSON::Pure::Parser.
+ # JSON::Ext::Parser or JSON::Pure::Parser:
+ # JSON.parser # => JSON::Ext::Parser
attr_reader :parser
# Set the JSON parser class _parser_ to be used by JSON.
@@ -84,15 +88,18 @@ module JSON
end
# Returns the JSON generator module that is used by JSON. This is
- # either JSON::Ext::Generator or JSON::Pure::Generator.
+ # either JSON::Ext::Generator or JSON::Pure::Generator:
+ # JSON.generator # => JSON::Ext::Generator
attr_reader :generator
- # Returns the JSON generator state class that is used by JSON. This is
- # either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
+ # 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
attr_accessor :state
- # This is create identifier, which is used to decide if the _json_create_
- # hook of a class should be called. It defaults to 'json_class'.
+ # 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"
attr_accessor :create_id
end
self.create_id = 'json_class'
@@ -126,7 +133,7 @@ module JSON
# This exception is raised if a generator or unparser error occurs.
class GeneratorError < JSONError; end
# For backwards compatibility
- UnparserError = GeneratorError
+ UnparserError = GeneratorError # :nodoc:
# This exception is raised if the required unicode support is missing on the
# system. Usually this means that the iconv library is not installed.
@@ -134,43 +141,158 @@ module JSON
module_function
- # Parse the JSON document _source_ into a Ruby data structure and return it.
- #
- # _opts_ can have the following
- # keys:
- # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
- # structures. Disable depth checking with :max_nesting => false. It
- # defaults to 100.
- # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
- # defiance of RFC 7159 to be parsed by the Parser. This option defaults
- # to false.
- # * *symbolize_names*: If set to true, returns symbols for the names
- # (keys) in a JSON object. Otherwise strings are returned. Strings are
- # the default.
- # * *create_additions*: If set to false, the Parser doesn't create
- # additions even if a matching class and create_id was found. This option
- # defaults to false.
- # * *object_class*: Defaults to Hash
- # * *array_class*: Defaults to Array
+ # Argument +source+ contains the \String to be parsed. It must be a
+ # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
+ # (implementing +to_str+), and must contain valid \JSON data.
+ #
+ # Argument +opts+, if given, contains options for the parsing, and must be a
+ # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
+ # (implementing +to_hash+).
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # ---
+ #
+ # 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}[#module-JSON-label-Parsing+JSON].
+ #
+ # ====== 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]
+ # With a truthy value:
+ # ruby = JSON.parse(source, {allow_nan: :foo})
+ # 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>
+ # Try class \Object:
+ # # Raises NoMethodError (undefined method `[]=' for #<Object:>):
+ # JSON.parse(source, {object_class: Object})
+ # Bad value:
+ # # Raises TypeError (wrong argument type Symbol (expected Class)):
+ # JSON.parse(source, {object_class: :foo})
+ #
+ # ---
+ #
+ # 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}>
+ # Try class \Object:
+ # # Raises NoMethodError (undefined method `<<' for #<Object:>):
+ # JSON.parse(source, {array_class: Object})
+ # Bad value:
+ # # Raises TypeError (wrong argument type Symbol (expected Class)):
+ # JSON.parse(source, {array_class: :foo})
+ #
+ # ---
+ #
+ # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
+ # See {\JSON Additions}[#module-JSON-label-JSON+Additions].
+ #
+ # ====== Exceptions
+ #
+ # Raises an exception if +source+ is not \String-convertible:
+ #
+ # # Raises TypeError (no implicit conversion of Symbol into String):
+ # JSON.parse(:foo)
+ #
+ # Raises an exception if +opts+ is not \Hash-convertible:
+ #
+ # # Raises TypeError (no implicit conversion of Symbol into Hash):
+ # JSON.parse(['foo'], :foo)
+ #
+ # Raises an exception if +source+ is not valid JSON:
+ #
+ # # Raises JSON::ParserError (783: unexpected token at ''):
+ # JSON.parse('')
+ #
def parse(source, opts = {})
Parser.new(source, **(opts||{})).parse
end
- # Parse the JSON document _source_ into a Ruby data structure and return it.
- # The bang version of the parse method defaults to the more dangerous values
- # for the _opts_ hash, so be sure only to parse trusted _source_ documents.
- #
- # _opts_ can have the following keys:
- # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
- # structures. Enable depth checking with :max_nesting => anInteger. The
- # parse! methods defaults to not doing max depth checking: This can be
- # dangerous if someone wants to fill up your stack.
- # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
- # defiance of RFC 7159 to be parsed by the Parser. This option defaults
- # to true.
- # * *create_additions*: If set to false, the Parser doesn't create
- # additions even if a matching class and create_id was found. This option
- # defaults to false.
+ # Calls
+ # JSON.parse(source, opts)
+ # with +source+ and possibly modified +opts+.
+ #
+ # Differences from JSON.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+.
def parse!(source, opts = {})
opts = {
:max_nesting => false,
@@ -179,35 +301,148 @@ module JSON
Parser.new(source, **(opts||{})).parse
end
- # Generate a JSON document from the Ruby data structure _obj_ and return
- # it. _opts_ is
- # * a Hash like object (responding to +to_hash+),
- # * or an object convertible into a hash by a +to_h+ method,
- # * or a <tt>JSON::State</tt> object.
- #
- # If hash-alike or hash-convertible object is provided, it is internally
- # converted into a State object.
- #
- # The default options are set to create the shortest possible JSON text
- # in one line, check for circular data structures and do not allow NaN,
- # Infinity, and -Infinity.
- #
- # An _opts_ hash can have the following keys:
- # * *indent*: a string used to indent levels (default: <tt>''</tt>),
- # * *space*: a string that is put after a <tt>:</tt> pair delimiter (default: <tt>''</tt>),
- # * *space_before*: a string that is put before a <tt>:</tt> pair delimiter (default: <tt>''</tt>),
- # * *object_nl*: a string that is put at the end of a JSON object (default: <tt>''</tt>),
- # * *array_nl*: a string that is put at the end of a JSON array (default: <tt>''</tt>),
- # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
- # generated, otherwise an exception is thrown if these values are
- # encountered. This options defaults to false.
- # * *max_nesting*: The maximum depth of nesting allowed in the data
- # structures from which JSON is to be generated. Disable depth checking
- # with <tt>max_nesting: false</tt>, it defaults to 100.
- #
- # See also the fast_generate for the fastest creation method with the least
- # amount of sanity checks, and the pretty_generate method for some
- # defaults for pretty output.
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
+ #
+ # Argument +opts+, if given, contains options for the generation, and must be a
+ # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
+ # (implementing +to_hash+).
+ #
+ # Returns a \String containing the generated \JSON data.
+ #
+ # See also JSON.fast_generate, JSON.pretty_generate.
+ #
+ # ---
+ #
+ # When +obj+ is an
+ # {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects]
+ # (implementing +to_ary+), 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]"
+ # json.class # => String
+ #
+ # When +obj+ is a
+ # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects],
+ # return 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}[#module-JSON-label-Generating+JSON+from+Other+Objects].
+ #
+ # ====== Input Options
+ #
+ # Option +allow_nan+ (boolean) specifies whether
+ # +NaN+, +Infinity+, and <tt>-Infinity</tt> 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 = [JSON::NaN, JSON::Infinity, JSON::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)
+ #
+ # Bad Value:
+ # # Raises TypeError (can't convert Symbol into Hash):
+ # JSON.generate(obj, :foo)
+ #
+ # ====== 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, <tt>''</tt>.
+ # - Option +object_nl+ (\String) specifies a string (usually a newline)
+ # to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
+ # - Option +indent+ (\String) specifies the string (usually spaces) to be
+ # used for indentation; defaults to the empty \String, <tt>''</tt>;
+ # defaults to the empty \String, <tt>''</tt>;
+ # has no effect unless options +array_nl+ or +object_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, <tt>''</tt>.
+ # - 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, <tt>''</tt>.
+ #
+ # 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
+ # }
+ # }
+ #
+ # ---
+ #
+ # Raises an exception if any formatting option is not a \String.
+ #
+ # ====== Exceptions
+ #
+ # Raises an exception if +obj+ is not a valid Ruby object:
+ # # Raises NameError (uninitialized constant Foo):
+ # JSON.generate(Foo)
+ # # Raises NameError (undefined local variable or method `foo' for main:Object):
+ # JSON.generate(foo)
+ #
+ # 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)
+ #
+ # Raises an exception if +opts is not a
+ # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
+ # (implementing +to_hash+):
+ # # Raises TypeError (can't convert Symbol into Hash):
+ # JSON.generate('x', :foo)
def generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
@@ -234,11 +469,16 @@ module JSON
module_function :unparse
# :startdoc:
- # Generate a JSON document from the Ruby data structure _obj_ and return it.
- # This method disables the checks for circles in Ruby objects.
+ # 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).
#
- # *WARNING*: Be careful not to pass any Ruby data structures with circles as
- # _obj_ argument because this will cause JSON to go into an infinite loop.
+ # 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)
def fast_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
@@ -264,12 +504,33 @@ module JSON
module_function :fast_unparse
# :startdoc:
- # Generate a JSON document from the Ruby data structure _obj_ and return it.
- # The returned document is a prettier form of the document returned by
- # #unparse.
+ # 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
+ # }
+ # }
#
- # The _opts_ argument can be used to configure the generator. See the
- # generate method for a more detailed explanation.
def pretty_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
@@ -296,10 +557,10 @@ module JSON
# :startdoc:
class << self
- # The global default options for the JSON.load method:
- # :max_nesting: false
- # :allow_nan: true
- # :allow_blank: true
+ # 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}
attr_accessor :load_default_options
end
self.load_default_options = {
@@ -358,10 +619,10 @@ module JSON
module_function :restore
class << self
- # The global default options for the JSON.dump method:
- # :max_nesting: false
- # :allow_nan: true
- # :allow_blank: 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}
attr_accessor :dump_default_options
end
self.dump_default_options = {
@@ -405,7 +666,7 @@ module JSON
raise ArgumentError, "exceed depth limit"
end
- # Encodes string using Ruby's _String.encode_
+ # Encodes string using String.encode.
def self.iconv(to, from, string)
string.encode(to, from)
end