json 1.8.6 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/{CHANGES → CHANGES.md} +292 -96
  3. data/LICENSE +56 -0
  4. data/README.md +185 -114
  5. data/ext/json/ext/fbuffer/fbuffer.h +0 -3
  6. data/ext/json/ext/generator/generator.c +328 -117
  7. data/ext/json/ext/generator/generator.h +8 -8
  8. data/ext/json/ext/parser/extconf.rb +29 -0
  9. data/ext/json/ext/parser/parser.c +540 -569
  10. data/ext/json/ext/parser/parser.h +10 -6
  11. data/ext/json/ext/parser/parser.rl +269 -261
  12. data/ext/json/extconf.rb +1 -1
  13. data/json.gemspec +0 -0
  14. data/lib/json/add/bigdecimal.rb +40 -10
  15. data/lib/json/add/complex.rb +32 -9
  16. data/lib/json/add/core.rb +1 -0
  17. data/lib/json/add/date.rb +27 -7
  18. data/lib/json/add/date_time.rb +26 -9
  19. data/lib/json/add/exception.rb +25 -7
  20. data/lib/json/add/ostruct.rb +32 -9
  21. data/lib/json/add/range.rb +33 -8
  22. data/lib/json/add/rational.rb +30 -8
  23. data/lib/json/add/regexp.rb +28 -10
  24. data/lib/json/add/set.rb +48 -0
  25. data/lib/json/add/struct.rb +29 -7
  26. data/lib/json/add/symbol.rb +28 -5
  27. data/lib/json/add/time.rb +27 -6
  28. data/lib/json/common.rb +402 -188
  29. data/lib/json/ext.rb +0 -6
  30. data/lib/json/generic_object.rb +11 -6
  31. data/lib/json/pure/generator.rb +120 -137
  32. data/lib/json/pure/parser.rb +64 -86
  33. data/lib/json/pure.rb +2 -8
  34. data/lib/json/version.rb +2 -1
  35. data/lib/json.rb +559 -29
  36. metadata +18 -129
  37. data/.gitignore +0 -17
  38. data/.travis.yml +0 -18
  39. data/Gemfile +0 -7
  40. data/README-json-jruby.markdown +0 -33
  41. data/Rakefile +0 -402
  42. data/TODO +0 -1
  43. data/VERSION +0 -1
  44. data/data/example.json +0 -1
  45. data/data/index.html +0 -38
  46. data/data/prototype.js +0 -4184
  47. data/diagrams/.keep +0 -0
  48. data/install.rb +0 -23
  49. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  50. data/java/src/json/ext/Generator.java +0 -446
  51. data/java/src/json/ext/GeneratorMethods.java +0 -231
  52. data/java/src/json/ext/GeneratorService.java +0 -42
  53. data/java/src/json/ext/GeneratorState.java +0 -542
  54. data/java/src/json/ext/OptionsReader.java +0 -113
  55. data/java/src/json/ext/Parser.java +0 -2644
  56. data/java/src/json/ext/Parser.rl +0 -968
  57. data/java/src/json/ext/ParserService.java +0 -34
  58. data/java/src/json/ext/RuntimeInfo.java +0 -120
  59. data/java/src/json/ext/StringDecoder.java +0 -166
  60. data/java/src/json/ext/StringEncoder.java +0 -111
  61. data/java/src/json/ext/Utils.java +0 -88
  62. data/json-java.gemspec +0 -38
  63. data/json_pure.gemspec +0 -37
  64. data/lib/json/ext/.keep +0 -0
  65. data/tests/fixtures/fail1.json +0 -1
  66. data/tests/fixtures/fail10.json +0 -1
  67. data/tests/fixtures/fail11.json +0 -1
  68. data/tests/fixtures/fail12.json +0 -1
  69. data/tests/fixtures/fail13.json +0 -1
  70. data/tests/fixtures/fail14.json +0 -1
  71. data/tests/fixtures/fail18.json +0 -1
  72. data/tests/fixtures/fail19.json +0 -1
  73. data/tests/fixtures/fail2.json +0 -1
  74. data/tests/fixtures/fail20.json +0 -1
  75. data/tests/fixtures/fail21.json +0 -1
  76. data/tests/fixtures/fail22.json +0 -1
  77. data/tests/fixtures/fail23.json +0 -1
  78. data/tests/fixtures/fail24.json +0 -1
  79. data/tests/fixtures/fail25.json +0 -1
  80. data/tests/fixtures/fail27.json +0 -2
  81. data/tests/fixtures/fail28.json +0 -2
  82. data/tests/fixtures/fail3.json +0 -1
  83. data/tests/fixtures/fail4.json +0 -1
  84. data/tests/fixtures/fail5.json +0 -1
  85. data/tests/fixtures/fail6.json +0 -1
  86. data/tests/fixtures/fail7.json +0 -1
  87. data/tests/fixtures/fail8.json +0 -1
  88. data/tests/fixtures/fail9.json +0 -1
  89. data/tests/fixtures/pass1.json +0 -56
  90. data/tests/fixtures/pass15.json +0 -1
  91. data/tests/fixtures/pass16.json +0 -1
  92. data/tests/fixtures/pass17.json +0 -1
  93. data/tests/fixtures/pass2.json +0 -1
  94. data/tests/fixtures/pass26.json +0 -1
  95. data/tests/fixtures/pass3.json +0 -6
  96. data/tests/setup_variant.rb +0 -11
  97. data/tests/test_json.rb +0 -519
  98. data/tests/test_json_addition.rb +0 -196
  99. data/tests/test_json_encoding.rb +0 -65
  100. data/tests/test_json_fixtures.rb +0 -35
  101. data/tests/test_json_generate.rb +0 -348
  102. data/tests/test_json_generic_object.rb +0 -75
  103. data/tests/test_json_string_matching.rb +0 -39
  104. data/tests/test_json_unicode.rb +0 -72
  105. data/tools/diff.sh +0 -18
  106. data/tools/fuzz.rb +0 -139
  107. data/tools/server.rb +0 -62
data/lib/json/common.rb CHANGED
@@ -1,14 +1,24 @@
1
+ #frozen_string_literal: false
1
2
  require 'json/version'
2
- require 'json/generic_object'
3
3
 
4
4
  module JSON
5
+ autoload :GenericObject, 'json/generic_object'
6
+
7
+ NOT_SET = Object.new.freeze
8
+ private_constant :NOT_SET
9
+
5
10
  class << self
6
- # If _object_ is string-like, parse the string and return the parsed result
7
- # as a Ruby data structure. Otherwise generate a JSON text from the Ruby
8
- # data structure object and return it.
11
+ # :call-seq:
12
+ # JSON[object] -> new_array or new_string
13
+ #
14
+ # If +object+ is a \String,
15
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
16
+ # json = '[0, 1, null]'
17
+ # JSON[json]# => [0, 1, nil]
9
18
  #
10
- # The _opts_ argument is passed through to generate/parse respectively. See
11
- # generate and parse for their documentation.
19
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
20
+ # ruby = [0, 1, nil]
21
+ # JSON[ruby] # => '[0,1,null]'
12
22
  def [](object, opts = {})
13
23
  if object.respond_to? :to_str
14
24
  JSON.parse(object.to_str, opts)
@@ -18,13 +28,14 @@ module JSON
18
28
  end
19
29
 
20
30
  # Returns the JSON parser class that is used by JSON. This is either
21
- # JSON::Ext::Parser or JSON::Pure::Parser.
31
+ # JSON::Ext::Parser or JSON::Pure::Parser:
32
+ # JSON.parser # => JSON::Ext::Parser
22
33
  attr_reader :parser
23
34
 
24
35
  # Set the JSON parser class _parser_ to be used by JSON.
25
36
  def parser=(parser) # :nodoc:
26
37
  @parser = parser
27
- remove_const :Parser if JSON.const_defined_in?(self, :Parser)
38
+ remove_const :Parser if const_defined?(:Parser, false)
28
39
  const_set :Parser, parser
29
40
  end
30
41
 
@@ -35,8 +46,8 @@ module JSON
35
46
  def deep_const_get(path) # :nodoc:
36
47
  path.to_s.split(/::/).inject(Object) do |p, c|
37
48
  case
38
- when c.empty? then p
39
- when JSON.const_defined_in?(p, c) then p.const_get(c)
49
+ when c.empty? then p
50
+ when p.const_defined?(c, true) then p.const_get(c)
40
51
  else
41
52
  begin
42
53
  p.const_missing(c)
@@ -64,37 +75,61 @@ module JSON
64
75
  end
65
76
  self.state = generator::State
66
77
  const_set :State, self.state
67
- const_set :SAFE_STATE_PROTOTYPE, State.new
68
- const_set :FAST_STATE_PROTOTYPE, State.new(
78
+ const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
79
+ const_set :FAST_STATE_PROTOTYPE, create_fast_state
80
+ const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
81
+ ensure
82
+ $VERBOSE = old
83
+ end
84
+
85
+ def create_fast_state
86
+ State.new(
69
87
  :indent => '',
70
88
  :space => '',
71
89
  :object_nl => "",
72
90
  :array_nl => "",
73
91
  :max_nesting => false
74
92
  )
75
- const_set :PRETTY_STATE_PROTOTYPE, State.new(
93
+ end
94
+
95
+ def create_pretty_state
96
+ State.new(
76
97
  :indent => ' ',
77
98
  :space => ' ',
78
99
  :object_nl => "\n",
79
100
  :array_nl => "\n"
80
101
  )
81
- ensure
82
- $VERBOSE = old
83
102
  end
84
103
 
85
104
  # Returns the JSON generator module that is used by JSON. This is
86
- # either JSON::Ext::Generator or JSON::Pure::Generator.
105
+ # either JSON::Ext::Generator or JSON::Pure::Generator:
106
+ # JSON.generator # => JSON::Ext::Generator
87
107
  attr_reader :generator
88
108
 
89
- # Returns the JSON generator state class that is used by JSON. This is
90
- # either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
109
+ # Sets or Returns the JSON generator state class that is used by JSON. This is
110
+ # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
111
+ # JSON.state # => JSON::Ext::Generator::State
91
112
  attr_accessor :state
113
+ end
92
114
 
93
- # This is create identifier, which is used to decide if the _json_create_
94
- # hook of a class should be called. It defaults to 'json_class'.
95
- attr_accessor :create_id
115
+ DEFAULT_CREATE_ID = 'json_class'.freeze
116
+ private_constant :DEFAULT_CREATE_ID
117
+
118
+ CREATE_ID_TLS_KEY = "JSON.create_id".freeze
119
+ private_constant :CREATE_ID_TLS_KEY
120
+
121
+ # Sets create identifier, which is used to decide if the _json_create_
122
+ # hook of a class should be called; initial value is +json_class+:
123
+ # JSON.create_id # => 'json_class'
124
+ def self.create_id=(new_value)
125
+ Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
126
+ end
127
+
128
+ # Returns the current create identifier.
129
+ # See also JSON.create_id=.
130
+ def self.create_id
131
+ Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
96
132
  end
97
- self.create_id = 'json_class'
98
133
 
99
134
  NaN = 0.0/0
100
135
 
@@ -125,7 +160,7 @@ module JSON
125
160
  # This exception is raised if a generator or unparser error occurs.
126
161
  class GeneratorError < JSONError; end
127
162
  # For backwards compatibility
128
- UnparserError = GeneratorError
163
+ UnparserError = GeneratorError # :nodoc:
129
164
 
130
165
  # This exception is raised if the required unicode support is missing on the
131
166
  # system. Usually this means that the iconv library is not installed.
@@ -133,92 +168,140 @@ module JSON
133
168
 
134
169
  module_function
135
170
 
136
- # Parse the JSON document _source_ into a Ruby data structure and return it.
137
- #
138
- # _opts_ can have the following
139
- # keys:
140
- # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
141
- # structures. Disable depth checking with :max_nesting => false. It defaults
142
- # to 100.
143
- # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
144
- # defiance of RFC 4627 to be parsed by the Parser. This option defaults
145
- # to false.
146
- # * *symbolize_names*: If set to true, returns symbols for the names
147
- # (keys) in a JSON object. Otherwise strings are returned. Strings are
148
- # the default.
149
- # * *create_additions*: If set to false, the Parser doesn't create
150
- # additions even if a matching class and create_id was found. This option
151
- # defaults to false.
152
- # * *object_class*: Defaults to Hash
153
- # * *array_class*: Defaults to Array
171
+ # :call-seq:
172
+ # JSON.parse(source, opts) -> object
173
+ #
174
+ # Returns the Ruby objects created by parsing the given +source+.
175
+ #
176
+ # Argument +source+ contains the \String to be parsed.
177
+ #
178
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
179
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
180
+ #
181
+ # ---
182
+ #
183
+ # When +source+ is a \JSON array, returns a Ruby \Array:
184
+ # source = '["foo", 1.0, true, false, null]'
185
+ # ruby = JSON.parse(source)
186
+ # ruby # => ["foo", 1.0, true, false, nil]
187
+ # ruby.class # => Array
188
+ #
189
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
190
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
191
+ # ruby = JSON.parse(source)
192
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
193
+ # ruby.class # => Hash
194
+ #
195
+ # For examples of parsing for all \JSON data types, see
196
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
197
+ #
198
+ # Parses nested JSON objects:
199
+ # source = <<-EOT
200
+ # {
201
+ # "name": "Dave",
202
+ # "age" :40,
203
+ # "hats": [
204
+ # "Cattleman's",
205
+ # "Panama",
206
+ # "Tophat"
207
+ # ]
208
+ # }
209
+ # EOT
210
+ # ruby = JSON.parse(source)
211
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
212
+ #
213
+ # ---
214
+ #
215
+ # Raises an exception if +source+ is not valid JSON:
216
+ # # Raises JSON::ParserError (783: unexpected token at ''):
217
+ # JSON.parse('')
218
+ #
154
219
  def parse(source, opts = {})
155
- Parser.new(source, opts).parse
220
+ Parser.new(source, **(opts||{})).parse
156
221
  end
157
222
 
158
- # Parse the JSON document _source_ into a Ruby data structure and return it.
159
- # The bang version of the parse method defaults to the more dangerous values
160
- # for the _opts_ hash, so be sure only to parse trusted _source_ documents.
161
- #
162
- # _opts_ can have the following keys:
163
- # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
164
- # structures. Enable depth checking with :max_nesting => anInteger. The parse!
165
- # methods defaults to not doing max depth checking: This can be dangerous
166
- # if someone wants to fill up your stack.
167
- # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
168
- # defiance of RFC 4627 to be parsed by the Parser. This option defaults
169
- # to true.
170
- # * *create_additions*: If set to false, the Parser doesn't create
171
- # additions even if a matching class and create_id was found. This option
172
- # defaults to false.
223
+ # :call-seq:
224
+ # JSON.parse!(source, opts) -> object
225
+ #
226
+ # Calls
227
+ # parse(source, opts)
228
+ # with +source+ and possibly modified +opts+.
229
+ #
230
+ # Differences from JSON.parse:
231
+ # - Option +max_nesting+, if not provided, defaults to +false+,
232
+ # which disables checking for nesting depth.
233
+ # - Option +allow_nan+, if not provided, defaults to +true+.
173
234
  def parse!(source, opts = {})
174
235
  opts = {
175
236
  :max_nesting => false,
176
237
  :allow_nan => true
177
- }.update(opts)
178
- Parser.new(source, opts).parse
238
+ }.merge(opts)
239
+ Parser.new(source, **(opts||{})).parse
240
+ end
241
+
242
+ # :call-seq:
243
+ # JSON.load_file(path, opts={}) -> object
244
+ #
245
+ # Calls:
246
+ # parse(File.read(path), opts)
247
+ #
248
+ # See method #parse.
249
+ def load_file(filespec, opts = {})
250
+ parse(File.read(filespec), opts)
251
+ end
252
+
253
+ # :call-seq:
254
+ # JSON.load_file!(path, opts = {})
255
+ #
256
+ # Calls:
257
+ # JSON.parse!(File.read(path, opts))
258
+ #
259
+ # See method #parse!
260
+ def load_file!(filespec, opts = {})
261
+ parse!(File.read(filespec), opts)
179
262
  end
180
263
 
181
- # Generate a JSON document from the Ruby data structure _obj_ and return
182
- # it. _state_ is * a JSON::State object,
183
- # * or a Hash like object (responding to to_hash),
184
- # * an object convertible into a hash by a to_h method,
185
- # that is used as or to configure a State object.
186
- #
187
- # It defaults to a state object, that creates the shortest possible JSON text
188
- # in one line, checks for circular data structures and doesn't allow NaN,
189
- # Infinity, and -Infinity.
190
- #
191
- # A _state_ hash can have the following keys:
192
- # * *indent*: a string used to indent levels (default: ''),
193
- # * *space*: a string that is put after, a : or , delimiter (default: ''),
194
- # * *space_before*: a string that is put before a : pair delimiter (default: ''),
195
- # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
196
- # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
197
- # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
198
- # generated, otherwise an exception is thrown if these values are
199
- # encountered. This options defaults to false.
200
- # * *max_nesting*: The maximum depth of nesting allowed in the data
201
- # structures from which JSON is to be generated. Disable depth checking
202
- # with :max_nesting => false, it defaults to 100.
203
- #
204
- # See also the fast_generate for the fastest creation method with the least
205
- # amount of sanity checks, and the pretty_generate method for some
206
- # defaults for pretty output.
264
+ # :call-seq:
265
+ # JSON.generate(obj, opts = nil) -> new_string
266
+ #
267
+ # Returns a \String containing the generated \JSON data.
268
+ #
269
+ # See also JSON.fast_generate, JSON.pretty_generate.
270
+ #
271
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
272
+ #
273
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
274
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
275
+ #
276
+ # ---
277
+ #
278
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
279
+ # obj = ["foo", 1.0, true, false, nil]
280
+ # json = JSON.generate(obj)
281
+ # json # => '["foo",1.0,true,false,null]'
282
+ #
283
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
284
+ # obj = {foo: 0, bar: 's', baz: :bat}
285
+ # json = JSON.generate(obj)
286
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
287
+ #
288
+ # For examples of generating from other Ruby objects, see
289
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
290
+ #
291
+ # ---
292
+ #
293
+ # Raises an exception if any formatting option is not a \String.
294
+ #
295
+ # Raises an exception if +obj+ contains circular references:
296
+ # a = []; b = []; a.push(b); b.push(a)
297
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
298
+ # JSON.generate(a)
299
+ #
207
300
  def generate(obj, opts = nil)
208
301
  if State === opts
209
- state, opts = opts, nil
302
+ state = opts
210
303
  else
211
- state = SAFE_STATE_PROTOTYPE.dup
212
- end
213
- if opts
214
- if opts.respond_to? :to_hash
215
- opts = opts.to_hash
216
- elsif opts.respond_to? :to_h
217
- opts = opts.to_h
218
- else
219
- raise TypeError, "can't convert #{opts.class} into Hash"
220
- end
221
- state = state.configure(opts)
304
+ state = State.new(opts)
222
305
  end
223
306
  state.generate(obj)
224
307
  end
@@ -230,26 +313,24 @@ module JSON
230
313
  module_function :unparse
231
314
  # :startdoc:
232
315
 
233
- # Generate a JSON document from the Ruby data structure _obj_ and return it.
234
- # This method disables the checks for circles in Ruby objects.
316
+ # :call-seq:
317
+ # JSON.fast_generate(obj, opts) -> new_string
318
+ #
319
+ # Arguments +obj+ and +opts+ here are the same as
320
+ # arguments +obj+ and +opts+ in JSON.generate.
235
321
  #
236
- # *WARNING*: Be careful not to pass any Ruby data structures with circles as
237
- # _obj_ argument because this will cause JSON to go into an infinite loop.
322
+ # By default, generates \JSON data without checking
323
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
324
+ #
325
+ # Raises an exception if +obj+ contains circular references:
326
+ # a = []; b = []; a.push(b); b.push(a)
327
+ # # Raises SystemStackError (stack level too deep):
328
+ # JSON.fast_generate(a)
238
329
  def fast_generate(obj, opts = nil)
239
330
  if State === opts
240
- state, opts = opts, nil
331
+ state = opts
241
332
  else
242
- state = FAST_STATE_PROTOTYPE.dup
243
- end
244
- if opts
245
- if opts.respond_to? :to_hash
246
- opts = opts.to_hash
247
- elsif opts.respond_to? :to_h
248
- opts = opts.to_h
249
- else
250
- raise TypeError, "can't convert #{opts.class} into Hash"
251
- end
252
- state.configure(opts)
333
+ state = JSON.create_fast_state.configure(opts)
253
334
  end
254
335
  state.generate(obj)
255
336
  end
@@ -260,17 +341,41 @@ module JSON
260
341
  module_function :fast_unparse
261
342
  # :startdoc:
262
343
 
263
- # Generate a JSON document from the Ruby data structure _obj_ and return it.
264
- # The returned document is a prettier form of the document returned by
265
- # #unparse.
344
+ # :call-seq:
345
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
346
+ #
347
+ # Arguments +obj+ and +opts+ here are the same as
348
+ # arguments +obj+ and +opts+ in JSON.generate.
349
+ #
350
+ # Default options are:
351
+ # {
352
+ # indent: ' ', # Two spaces
353
+ # space: ' ', # One space
354
+ # array_nl: "\n", # Newline
355
+ # object_nl: "\n" # Newline
356
+ # }
357
+ #
358
+ # Example:
359
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
360
+ # json = JSON.pretty_generate(obj)
361
+ # puts json
362
+ # Output:
363
+ # {
364
+ # "foo": [
365
+ # "bar",
366
+ # "baz"
367
+ # ],
368
+ # "bat": {
369
+ # "bam": 0,
370
+ # "bad": 1
371
+ # }
372
+ # }
266
373
  #
267
- # The _opts_ argument can be used to configure the generator. See the
268
- # generate method for a more detailed explanation.
269
374
  def pretty_generate(obj, opts = nil)
270
375
  if State === opts
271
376
  state, opts = opts, nil
272
377
  else
273
- state = PRETTY_STATE_PROTOTYPE.dup
378
+ state = JSON.create_pretty_state
274
379
  end
275
380
  if opts
276
381
  if opts.respond_to? :to_hash
@@ -292,33 +397,147 @@ module JSON
292
397
  # :startdoc:
293
398
 
294
399
  class << self
295
- # The global default options for the JSON.load method:
296
- # :max_nesting: false
297
- # :allow_nan: true
298
- # :quirks_mode: true
400
+ # Sets or returns default options for the JSON.load method.
401
+ # Initially:
402
+ # opts = JSON.load_default_options
403
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
299
404
  attr_accessor :load_default_options
300
405
  end
301
406
  self.load_default_options = {
302
407
  :max_nesting => false,
303
408
  :allow_nan => true,
304
- :quirks_mode => true,
409
+ :allow_blank => true,
305
410
  :create_additions => true,
306
411
  }
307
412
 
308
- # Load a ruby data structure from a JSON _source_ and return it. A source can
309
- # either be a string-like object, an IO-like object, or an object responding
310
- # to the read method. If _proc_ was given, it will be called with any nested
311
- # Ruby object as an argument recursively in depth first order. To modify the
312
- # default options pass in the optional _options_ argument as well.
413
+ # :call-seq:
414
+ # JSON.load(source, proc = nil, options = {}) -> object
415
+ #
416
+ # Returns the Ruby objects created by parsing the given +source+.
417
+ #
418
+ # - Argument +source+ must be, or be convertible to, a \String:
419
+ # - If +source+ responds to instance method +to_str+,
420
+ # <tt>source.to_str</tt> becomes the source.
421
+ # - If +source+ responds to instance method +to_io+,
422
+ # <tt>source.to_io.read</tt> becomes the source.
423
+ # - If +source+ responds to instance method +read+,
424
+ # <tt>source.read</tt> becomes the source.
425
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
426
+ # - Option +allow_blank+ specifies a truthy value.
427
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
428
+ # - Otherwise, +source+ remains the source.
429
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
430
+ # It will be called recursively with each result (depth-first order).
431
+ # See details below.
432
+ # BEWARE: This method is meant to serialise data from trusted user input,
433
+ # like from your own database server or clients under your control, it could
434
+ # be dangerous to allow untrusted users to pass JSON sources into it.
435
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
436
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
437
+ # The default options can be changed via method JSON.load_default_options=.
438
+ #
439
+ # ---
440
+ #
441
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
442
+ # <tt>parse(source, opts)</tt>; see #parse.
443
+ #
444
+ # Source for following examples:
445
+ # source = <<-EOT
446
+ # {
447
+ # "name": "Dave",
448
+ # "age" :40,
449
+ # "hats": [
450
+ # "Cattleman's",
451
+ # "Panama",
452
+ # "Tophat"
453
+ # ]
454
+ # }
455
+ # EOT
456
+ #
457
+ # Load a \String:
458
+ # ruby = JSON.load(source)
459
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
460
+ #
461
+ # Load an \IO object:
462
+ # require 'stringio'
463
+ # object = JSON.load(StringIO.new(source))
464
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
465
+ #
466
+ # Load a \File object:
467
+ # path = 't.json'
468
+ # File.write(path, source)
469
+ # File.open(path) do |file|
470
+ # JSON.load(file)
471
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
472
+ #
473
+ # ---
313
474
  #
314
- # BEWARE: This method is meant to serialise data from trusted user input,
315
- # like from your own database server or clients under your control, it could
316
- # be dangerous to allow untrusted users to pass JSON sources into it. The
317
- # default options for the parser can be changed via the load_default_options
318
- # method.
475
+ # When +proc+ is given:
476
+ # - Modifies +source+ as above.
477
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
478
+ # - Recursively calls <tt>proc(result)</tt>.
479
+ # - Returns the final result.
480
+ #
481
+ # Example:
482
+ # require 'json'
483
+ #
484
+ # # Some classes for the example.
485
+ # class Base
486
+ # def initialize(attributes)
487
+ # @attributes = attributes
488
+ # end
489
+ # end
490
+ # class User < Base; end
491
+ # class Account < Base; end
492
+ # class Admin < Base; end
493
+ # # The JSON source.
494
+ # json = <<-EOF
495
+ # {
496
+ # "users": [
497
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
498
+ # {"type": "User", "username": "john", "email": "john@example.com"}
499
+ # ],
500
+ # "accounts": [
501
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
502
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
503
+ # ],
504
+ # "admins": {"type": "Admin", "password": "0wn3d"}
505
+ # }
506
+ # EOF
507
+ # # Deserializer method.
508
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
509
+ # type = obj.is_a?(Hash) && obj["type"]
510
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
511
+ # end
512
+ # # Call to JSON.load
513
+ # ruby = JSON.load(json, proc {|obj|
514
+ # case obj
515
+ # when Hash
516
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
517
+ # when Array
518
+ # obj.map! {|v| deserialize_obj v }
519
+ # end
520
+ # })
521
+ # pp ruby
522
+ # Output:
523
+ # {"users"=>
524
+ # [#<User:0x00000000064c4c98
525
+ # @attributes=
526
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
527
+ # #<User:0x00000000064c4bd0
528
+ # @attributes=
529
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
530
+ # "accounts"=>
531
+ # [{"account"=>
532
+ # #<Account:0x00000000064c4928
533
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
534
+ # {"account"=>
535
+ # #<Account:0x00000000064c4680
536
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
537
+ # "admins"=>
538
+ # #<Admin:0x00000000064c41f8
539
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
319
540
  #
320
- # This method is part of the implementation of the load/dump interface of
321
- # Marshal and YAML.
322
541
  def load(source, proc = nil, options = {})
323
542
  opts = load_default_options.merge options
324
543
  if source.respond_to? :to_str
@@ -328,7 +547,7 @@ module JSON
328
547
  elsif source.respond_to?(:read)
329
548
  source = source.read
330
549
  end
331
- if opts[:quirks_mode] && (source.nil? || source.empty?)
550
+ if opts[:allow_blank] && (source.nil? || source.empty?)
332
551
  source = 'null'
333
552
  end
334
553
  result = parse(source, opts)
@@ -337,7 +556,7 @@ module JSON
337
556
  end
338
557
 
339
558
  # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
340
- def recurse_proc(result, &proc)
559
+ def recurse_proc(result, &proc) # :nodoc:
341
560
  case result
342
561
  when Array
343
562
  result.each { |x| recurse_proc x, &proc }
@@ -354,43 +573,57 @@ module JSON
354
573
  module_function :restore
355
574
 
356
575
  class << self
357
- # The global default options for the JSON.dump method:
358
- # :max_nesting: false
359
- # :allow_nan: true
360
- # :quirks_mode: true
576
+ # Sets or returns the default options for the JSON.dump method.
577
+ # Initially:
578
+ # opts = JSON.dump_default_options
579
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :script_safe=>false}
361
580
  attr_accessor :dump_default_options
362
581
  end
363
582
  self.dump_default_options = {
364
583
  :max_nesting => false,
365
584
  :allow_nan => true,
366
- :quirks_mode => true,
585
+ :script_safe => false,
367
586
  }
368
587
 
369
- # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
370
- # the result.
588
+ # :call-seq:
589
+ # JSON.dump(obj, io = nil, limit = nil)
371
590
  #
372
- # If anIO (an IO-like object or an object that responds to the write method)
373
- # was given, the resulting JSON is written to it.
591
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
374
592
  #
375
- # If the number of nested arrays or objects exceeds _limit_, an ArgumentError
376
- # exception is raised. This argument is similar (but not exactly the
377
- # same!) to the _limit_ argument in Marshal.dump.
593
+ # The default options can be changed via method JSON.dump_default_options.
378
594
  #
379
- # The default options for the generator can be changed via the
380
- # dump_default_options method.
595
+ # - Argument +io+, if given, should respond to method +write+;
596
+ # the \JSON \String is written to +io+, and +io+ is returned.
597
+ # If +io+ is not given, the \JSON \String is returned.
598
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
381
599
  #
382
- # This method is part of the implementation of the load/dump interface of
383
- # Marshal and YAML.
384
- def dump(obj, anIO = nil, limit = nil)
385
- if anIO and limit.nil?
386
- anIO = anIO.to_io if anIO.respond_to?(:to_io)
387
- unless anIO.respond_to?(:write)
388
- limit = anIO
389
- anIO = nil
390
- end
600
+ # ---
601
+ #
602
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
603
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
604
+ # json = JSON.dump(obj)
605
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
606
+ #
607
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
608
+ # path = 't.json'
609
+ # File.open(path, 'w') do |file|
610
+ # JSON.dump(obj, file)
611
+ # end # => #<File:t.json (closed)>
612
+ # puts File.read(path)
613
+ # Output:
614
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
615
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
616
+ io_limit_opt = [anIO, limit, kwargs].compact
617
+ kwargs = io_limit_opt.pop if io_limit_opt.last.is_a?(Hash)
618
+ anIO, limit = io_limit_opt
619
+ if anIO.respond_to?(:to_io)
620
+ anIO = anIO.to_io
621
+ elsif limit.nil? && !anIO.respond_to?(:write)
622
+ anIO, limit = nil, anIO
391
623
  end
392
624
  opts = JSON.dump_default_options
393
625
  opts = opts.merge(:max_nesting => limit) if limit
626
+ opts = merge_dump_options(opts, **kwargs) if kwargs
394
627
  result = generate(obj, opts)
395
628
  if anIO
396
629
  anIO.write result
@@ -402,37 +635,18 @@ module JSON
402
635
  raise ArgumentError, "exceed depth limit"
403
636
  end
404
637
 
405
- # Swap consecutive bytes of _string_ in place.
406
- def self.swap!(string) # :nodoc:
407
- 0.upto(string.size / 2) do |i|
408
- break unless string[2 * i + 1]
409
- string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
410
- end
411
- string
638
+ # Encodes string using String.encode.
639
+ def self.iconv(to, from, string)
640
+ string.encode(to, from)
412
641
  end
413
642
 
414
- # Shortcut for iconv.
415
- if ::String.method_defined?(:encode)
416
- # Encodes string using Ruby's _String.encode_
417
- def self.iconv(to, from, string)
418
- string.encode(to, from)
419
- end
420
- else
421
- require 'iconv'
422
- # Encodes string using _iconv_ library
423
- def self.iconv(to, from, string)
424
- Iconv.conv(to, from, string)
425
- end
643
+ def merge_dump_options(opts, strict: NOT_SET)
644
+ opts = opts.merge(strict: strict) if NOT_SET != strict
645
+ opts
426
646
  end
427
647
 
428
- if ::Object.method(:const_defined?).arity == 1
429
- def self.const_defined_in?(modul, constant)
430
- modul.const_defined?(constant)
431
- end
432
- else
433
- def self.const_defined_in?(modul, constant)
434
- modul.const_defined?(constant, false)
435
- end
648
+ class << self
649
+ private :merge_dump_options
436
650
  end
437
651
  end
438
652