json 2.3.1 → 2.5.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +22 -0
  3. data/LICENSE +56 -0
  4. data/VERSION +1 -1
  5. data/ext/json/ext/generator/generator.c +60 -11
  6. data/ext/json/ext/generator/generator.h +5 -2
  7. data/ext/json/ext/parser/extconf.rb +25 -0
  8. data/ext/json/ext/parser/parser.c +110 -68
  9. data/ext/json/ext/parser/parser.h +1 -0
  10. data/ext/json/ext/parser/parser.rl +67 -25
  11. data/ext/json/extconf.rb +1 -0
  12. data/json.gemspec +11 -77
  13. data/lib/json.rb +171 -0
  14. data/lib/json/add/complex.rb +0 -1
  15. data/lib/json/add/rational.rb +0 -1
  16. data/lib/json/common.rb +240 -228
  17. data/lib/json/pure/generator.rb +28 -8
  18. data/lib/json/pure/parser.rb +20 -2
  19. data/lib/json/version.rb +1 -1
  20. data/tests/fixtures/fail29.json +1 -0
  21. data/tests/fixtures/fail30.json +1 -0
  22. data/tests/fixtures/fail31.json +1 -0
  23. data/tests/fixtures/fail32.json +1 -0
  24. data/tests/json_addition_test.rb +0 -4
  25. data/tests/json_common_interface_test.rb +43 -0
  26. data/tests/json_fixtures_test.rb +3 -0
  27. data/tests/json_generator_test.rb +16 -38
  28. data/tests/json_parser_test.rb +25 -0
  29. data/tests/lib/core_assertions.rb +763 -0
  30. data/tests/lib/envutil.rb +365 -0
  31. data/tests/lib/find_executable.rb +22 -0
  32. data/tests/lib/helper.rb +4 -0
  33. data/tests/ractor_test.rb +30 -0
  34. data/tests/test_helper.rb +3 -3
  35. metadata +16 -37
  36. data/.gitignore +0 -18
  37. data/.travis.yml +0 -26
  38. data/README-json-jruby.md +0 -33
  39. data/Rakefile +0 -334
  40. data/diagrams/.keep +0 -0
  41. data/install.rb +0 -23
  42. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  43. data/java/src/json/ext/Generator.java +0 -466
  44. data/java/src/json/ext/GeneratorMethods.java +0 -231
  45. data/java/src/json/ext/GeneratorService.java +0 -42
  46. data/java/src/json/ext/GeneratorState.java +0 -490
  47. data/java/src/json/ext/OptionsReader.java +0 -113
  48. data/java/src/json/ext/Parser.java +0 -2362
  49. data/java/src/json/ext/Parser.rl +0 -893
  50. data/java/src/json/ext/ParserService.java +0 -34
  51. data/java/src/json/ext/RuntimeInfo.java +0 -116
  52. data/java/src/json/ext/StringDecoder.java +0 -166
  53. data/java/src/json/ext/StringEncoder.java +0 -111
  54. data/java/src/json/ext/Utils.java +0 -88
  55. data/json-java.gemspec +0 -37
  56. data/json_pure.gemspec +0 -33
  57. data/references/rfc7159.txt +0 -899
  58. data/tools/diff.sh +0 -18
  59. data/tools/fuzz.rb +0 -131
  60. data/tools/server.rb +0 -62
@@ -2,7 +2,6 @@
2
2
  unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
3
3
  require 'json'
4
4
  end
5
- defined?(::Complex) or require 'complex'
6
5
 
7
6
  class Complex
8
7
 
@@ -2,7 +2,6 @@
2
2
  unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
3
3
  require 'json'
4
4
  end
5
- defined?(::Rational) or require 'rational'
6
5
 
7
6
  class Rational
8
7
  # Deserializes JSON string by converting numerator value <tt>n</tt>,
@@ -4,13 +4,15 @@ require 'json/generic_object'
4
4
 
5
5
  module JSON
6
6
  class << self
7
- # If +object+ is a
8
- # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
9
- # (implementing +to_str+), calls JSON.parse with +object+ and +opts+:
7
+ # :call-seq:
8
+ # JSON[object] -> new_array or new_string
9
+ #
10
+ # If +object+ is a \String,
11
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
10
12
  # json = '[0, 1, null]'
11
13
  # JSON[json]# => [0, 1, nil]
12
14
  #
13
- # Otherwise, calls JSON.generate with +object+ and +opts+:
15
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
14
16
  # ruby = [0, 1, nil]
15
17
  # JSON[ruby] # => '[0,1,null]'
16
18
  def [](object, opts = {})
@@ -69,22 +71,30 @@ module JSON
69
71
  end
70
72
  self.state = generator::State
71
73
  const_set :State, self.state
72
- const_set :SAFE_STATE_PROTOTYPE, State.new
73
- const_set :FAST_STATE_PROTOTYPE, State.new(
74
+ const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
75
+ const_set :FAST_STATE_PROTOTYPE, create_fast_state
76
+ const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
77
+ ensure
78
+ $VERBOSE = old
79
+ end
80
+
81
+ def create_fast_state
82
+ State.new(
74
83
  :indent => '',
75
84
  :space => '',
76
85
  :object_nl => "",
77
86
  :array_nl => "",
78
87
  :max_nesting => false
79
88
  )
80
- const_set :PRETTY_STATE_PROTOTYPE, State.new(
89
+ end
90
+
91
+ def create_pretty_state
92
+ State.new(
81
93
  :indent => ' ',
82
94
  :space => ' ',
83
95
  :object_nl => "\n",
84
96
  :array_nl => "\n"
85
97
  )
86
- ensure
87
- $VERBOSE = old
88
98
  end
89
99
 
90
100
  # Returns the JSON generator module that is used by JSON. This is
@@ -96,13 +106,26 @@ module JSON
96
106
  # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
97
107
  # JSON.state # => JSON::Ext::Generator::State
98
108
  attr_accessor :state
109
+ end
110
+
111
+ DEFAULT_CREATE_ID = 'json_class'.freeze
112
+ private_constant :DEFAULT_CREATE_ID
113
+
114
+ CREATE_ID_TLS_KEY = "JSON.create_id".freeze
115
+ private_constant :CREATE_ID_TLS_KEY
116
+
117
+ # Sets create identifier, which is used to decide if the _json_create_
118
+ # hook of a class should be called; initial value is +json_class+:
119
+ # JSON.create_id # => 'json_class'
120
+ def self.create_id=(new_value)
121
+ Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
122
+ end
99
123
 
100
- # Sets or returns create identifier, which is used to decide if the _json_create_
101
- # hook of a class should be called; initial value is +json_class+:
102
- # JSON.create_id # => 'json_class'
103
- attr_accessor :create_id
124
+ # Returns the current create identifier.
125
+ # See also JSON.create_id=.
126
+ def self.create_id
127
+ Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
104
128
  end
105
- self.create_id = 'json_class'
106
129
 
107
130
  NaN = 0.0/0
108
131
 
@@ -144,15 +167,12 @@ module JSON
144
167
  # :call-seq:
145
168
  # JSON.parse(source, opts) -> object
146
169
  #
147
- # Argument +source+ contains the \String to be parsed. It must be a
148
- # {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
149
- # (implementing +to_str+), and must contain valid \JSON data.
170
+ # Returns the Ruby objects created by parsing the given +source+.
150
171
  #
151
- # Argument +opts+, if given, contains options for the parsing, and must be a
152
- # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
153
- # (implementing +to_hash+).
172
+ # Argument +source+ contains the \String to be parsed.
154
173
  #
155
- # Returns the Ruby objects created by parsing the given +source+.
174
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
175
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
156
176
  #
157
177
  # ---
158
178
  #
@@ -171,91 +191,24 @@ module JSON
171
191
  # For examples of parsing for all \JSON data types, see
172
192
  # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
173
193
  #
174
- # ====== Input Options
175
- #
176
- # Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
177
- # defaults to +100+; specify +false+ to disable depth checking.
178
- #
179
- # With the default, +false+:
180
- # source = '[0, [1, [2, [3]]]]'
181
- # ruby = JSON.parse(source)
182
- # ruby # => [0, [1, [2, [3]]]]
183
- # Too deep:
184
- # # Raises JSON::NestingError (nesting of 2 is too deep):
185
- # JSON.parse(source, {max_nesting: 1})
186
- # Bad value:
187
- # # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
188
- # JSON.parse(source, {max_nesting: :foo})
189
- #
190
- # ---
191
- #
192
- # Option +allow_nan+ (boolean) specifies whether to allow
193
- # NaN, Infinity, and MinusInfinity in +source+;
194
- # defaults to +false+.
195
- #
196
- # With the default, +false+:
197
- # # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
198
- # JSON.parse('[NaN]')
199
- # # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
200
- # JSON.parse('[Infinity]')
201
- # # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
202
- # JSON.parse('[-Infinity]')
203
- # Allow:
204
- # source = '[NaN, Infinity, -Infinity]'
205
- # ruby = JSON.parse(source, {allow_nan: true})
206
- # ruby # => [NaN, Infinity, -Infinity]
207
- #
208
- # ====== Output Options
209
- #
210
- # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
211
- # should be Symbols;
212
- # defaults to +false+ (use Strings).
213
- #
214
- # With the default, +false+:
215
- # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
216
- # ruby = JSON.parse(source)
217
- # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
218
- # Use Symbols:
219
- # ruby = JSON.parse(source, {symbolize_names: true})
220
- # ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
221
- #
222
- # ---
223
- #
224
- # Option +object_class+ (\Class) specifies the Ruby class to be used
225
- # for each \JSON object;
226
- # defaults to \Hash.
227
- #
228
- # With the default, \Hash:
229
- # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
230
- # ruby = JSON.parse(source)
231
- # ruby.class # => Hash
232
- # Use class \OpenStruct:
233
- # ruby = JSON.parse(source, {object_class: OpenStruct})
234
- # ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
235
- #
236
- # ---
237
- #
238
- # Option +array_class+ (\Class) specifies the Ruby class to be used
239
- # for each \JSON array;
240
- # defaults to \Array.
241
- #
242
- # With the default, \Array:
243
- # source = '["foo", 1.0, true, false, null]'
194
+ # Parses nested JSON objects:
195
+ # source = <<-EOT
196
+ # {
197
+ # "name": "Dave",
198
+ # "age" :40,
199
+ # "hats": [
200
+ # "Cattleman's",
201
+ # "Panama",
202
+ # "Tophat"
203
+ # ]
204
+ # }
205
+ # EOT
244
206
  # ruby = JSON.parse(source)
245
- # ruby.class # => Array
246
- # Use class \Set:
247
- # ruby = JSON.parse(source, {array_class: Set})
248
- # ruby # => #<Set: {"foo", 1.0, true, false, nil}>
207
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
249
208
  #
250
209
  # ---
251
210
  #
252
- # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
253
- # See {\JSON Additions}[#module-JSON-label-JSON+Additions].
254
- #
255
- # ====== Exceptions
256
- #
257
211
  # Raises an exception if +source+ is not valid JSON:
258
- #
259
212
  # # Raises JSON::ParserError (783: unexpected token at ''):
260
213
  # JSON.parse('')
261
214
  #
@@ -283,30 +236,47 @@ module JSON
283
236
  end
284
237
 
285
238
  # :call-seq:
286
- # JSON.generate(obj, opts = nil) -> new_string
239
+ # JSON.load_file(path, opts={}) -> object
287
240
  #
288
- # Argument +obj+ is the Ruby object to be converted to \JSON.
241
+ # Calls:
242
+ # parse(File.read(path), opts)
289
243
  #
290
- # Argument +opts+, if given, contains options for the generation, and must be a
291
- # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
292
- # (implementing +to_hash+).
244
+ # See method #parse.
245
+ def load_file(filespec, opts = {})
246
+ parse(File.read(filespec), opts)
247
+ end
248
+
249
+ # :call-seq:
250
+ # JSON.load_file!(path, opts = {})
251
+ #
252
+ # Calls:
253
+ # JSON.parse!(File.read(path, opts))
254
+ #
255
+ # See method #parse!
256
+ def load_file!(filespec, opts = {})
257
+ parse!(File.read(filespec), opts)
258
+ end
259
+
260
+ # :call-seq:
261
+ # JSON.generate(obj, opts = nil) -> new_string
293
262
  #
294
263
  # Returns a \String containing the generated \JSON data.
295
264
  #
296
265
  # See also JSON.fast_generate, JSON.pretty_generate.
297
266
  #
267
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
268
+ #
269
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
270
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
271
+ #
298
272
  # ---
299
273
  #
300
- # When +obj+ is an
301
- # {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects]
302
- # (implementing +to_ary+), returns a \String containing a \JSON array:
274
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
303
275
  # obj = ["foo", 1.0, true, false, nil]
304
276
  # json = JSON.generate(obj)
305
277
  # json # => '["foo",1.0,true,false,null]'
306
278
  #
307
- # When +obj+ is a
308
- # {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects],
309
- # return a \String containing a \JSON object:
279
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
310
280
  # obj = {foo: 0, bar: 's', baz: :bat}
311
281
  # json = JSON.generate(obj)
312
282
  # json # => '{"foo":0,"bar":"s","baz":"bat"}'
@@ -314,98 +284,10 @@ module JSON
314
284
  # For examples of generating from other Ruby objects, see
315
285
  # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
316
286
  #
317
- # ====== Input Options
318
- #
319
- # Option +allow_nan+ (boolean) specifies whether
320
- # +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
321
- # defaults to +false+.
322
- #
323
- # With the default, +false+:
324
- # # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
325
- # JSON.generate(JSON::NaN)
326
- # # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
327
- # JSON.generate(JSON::Infinity)
328
- # # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
329
- # JSON.generate(JSON::MinusInfinity)
330
- #
331
- # Allow:
332
- # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
333
- # JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
334
- #
335
- # ---
336
- #
337
- # Option +max_nesting+ (\Integer) specifies the maximum nesting depth
338
- # in +obj+; defaults to +100+.
339
- #
340
- # With the default, +100+:
341
- # obj = [[[[[[0]]]]]]
342
- # JSON.generate(obj) # => '[[[[[[0]]]]]]'
343
- #
344
- # Too deep:
345
- # # Raises JSON::NestingError (nesting of 2 is too deep):
346
- # JSON.generate(obj, max_nesting: 2)
347
- #
348
- # ====== Output Options
349
- #
350
- # The default formatting options generate the most compact
351
- # \JSON data, all on one line and with no whitespace.
352
- #
353
- # You can use these formatting options to generate
354
- # \JSON data in a more open format, using whitespace.
355
- # See also JSON.pretty_generate.
356
- #
357
- # - Option +array_nl+ (\String) specifies a string (usually a newline)
358
- # to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
359
- # - Option +object_nl+ (\String) specifies a string (usually a newline)
360
- # to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
361
- # - Option +indent+ (\String) specifies the string (usually spaces) to be
362
- # used for indentation; defaults to the empty \String, <tt>''</tt>;
363
- # defaults to the empty \String, <tt>''</tt>;
364
- # has no effect unless options +array_nl+ or +object_nl+ specify newlines.
365
- # - Option +space+ (\String) specifies a string (usually a space) to be
366
- # inserted after the colon in each \JSON object's pair;
367
- # defaults to the empty \String, <tt>''</tt>.
368
- # - Option +space_before+ (\String) specifies a string (usually a space) to be
369
- # inserted before the colon in each \JSON object's pair;
370
- # defaults to the empty \String, <tt>''</tt>.
371
- #
372
- # In this example, +obj+ is used first to generate the shortest
373
- # \JSON data (no whitespace), then again with all formatting options
374
- # specified:
375
- #
376
- # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
377
- # json = JSON.generate(obj)
378
- # puts 'Compact:', json
379
- # opts = {
380
- # array_nl: "\n",
381
- # object_nl: "\n",
382
- # indent+: ' ',
383
- # space_before: ' ',
384
- # space: ' '
385
- # }
386
- # puts 'Open:', JSON.generate(obj, opts)
387
- #
388
- # Output:
389
- # Compact:
390
- # {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
391
- # Open:
392
- # {
393
- # "foo" : [
394
- # "bar",
395
- # "baz"
396
- # ],
397
- # "bat" : {
398
- # "bam" : 0,
399
- # "bad" : 1
400
- # }
401
- # }
402
- #
403
287
  # ---
404
288
  #
405
289
  # Raises an exception if any formatting option is not a \String.
406
290
  #
407
- # ====== Exceptions
408
- #
409
291
  # Raises an exception if +obj+ contains circular references:
410
292
  # a = []; b = []; a.push(b); b.push(a)
411
293
  # # Raises JSON::NestingError (nesting of 100 is too deep):
@@ -415,7 +297,7 @@ module JSON
415
297
  if State === opts
416
298
  state, opts = opts, nil
417
299
  else
418
- state = SAFE_STATE_PROTOTYPE.dup
300
+ state = State.new
419
301
  end
420
302
  if opts
421
303
  if opts.respond_to? :to_hash
@@ -437,6 +319,9 @@ module JSON
437
319
  module_function :unparse
438
320
  # :startdoc:
439
321
 
322
+ # :call-seq:
323
+ # JSON.fast_generate(obj, opts) -> new_string
324
+ #
440
325
  # Arguments +obj+ and +opts+ here are the same as
441
326
  # arguments +obj+ and +opts+ in JSON.generate.
442
327
  #
@@ -451,7 +336,7 @@ module JSON
451
336
  if State === opts
452
337
  state, opts = opts, nil
453
338
  else
454
- state = FAST_STATE_PROTOTYPE.dup
339
+ state = JSON.create_fast_state
455
340
  end
456
341
  if opts
457
342
  if opts.respond_to? :to_hash
@@ -506,7 +391,7 @@ module JSON
506
391
  if State === opts
507
392
  state, opts = opts, nil
508
393
  else
509
- state = PRETTY_STATE_PROTOTYPE.dup
394
+ state = JSON.create_pretty_state
510
395
  end
511
396
  if opts
512
397
  if opts.respond_to? :to_hash
@@ -541,20 +426,134 @@ module JSON
541
426
  :create_additions => true,
542
427
  }
543
428
 
544
- # Load a ruby data structure from a JSON _source_ and return it. A source can
545
- # either be a string-like object, an IO-like object, or an object responding
546
- # to the read method. If _proc_ was given, it will be called with any nested
547
- # Ruby object as an argument recursively in depth first order. To modify the
548
- # default options pass in the optional _options_ argument as well.
429
+ # :call-seq:
430
+ # JSON.load(source, proc = nil, options = {}) -> object
431
+ #
432
+ # Returns the Ruby objects created by parsing the given +source+.
433
+ #
434
+ # - Argument +source+ must be, or be convertible to, a \String:
435
+ # - If +source+ responds to instance method +to_str+,
436
+ # <tt>source.to_str</tt> becomes the source.
437
+ # - If +source+ responds to instance method +to_io+,
438
+ # <tt>source.to_io.read</tt> becomes the source.
439
+ # - If +source+ responds to instance method +read+,
440
+ # <tt>source.read</tt> becomes the source.
441
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
442
+ # - Option +allow_blank+ specifies a truthy value.
443
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
444
+ # - Otherwise, +source+ remains the source.
445
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
446
+ # It will be called recursively with each result (depth-first order).
447
+ # See details below.
448
+ # BEWARE: This method is meant to serialise data from trusted user input,
449
+ # like from your own database server or clients under your control, it could
450
+ # be dangerous to allow untrusted users to pass JSON sources into it.
451
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
452
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
453
+ # The default options can be changed via method JSON.load_default_options=.
454
+ #
455
+ # ---
456
+ #
457
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
458
+ # <tt>parse(source, opts)</tt>; see #parse.
459
+ #
460
+ # Source for following examples:
461
+ # source = <<-EOT
462
+ # {
463
+ # "name": "Dave",
464
+ # "age" :40,
465
+ # "hats": [
466
+ # "Cattleman's",
467
+ # "Panama",
468
+ # "Tophat"
469
+ # ]
470
+ # }
471
+ # EOT
472
+ #
473
+ # Load a \String:
474
+ # ruby = JSON.load(source)
475
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
476
+ #
477
+ # Load an \IO object:
478
+ # require 'stringio'
479
+ # object = JSON.load(StringIO.new(source))
480
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
481
+ #
482
+ # Load a \File object:
483
+ # path = 't.json'
484
+ # File.write(path, source)
485
+ # File.open(path) do |file|
486
+ # JSON.load(file)
487
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
549
488
  #
550
- # BEWARE: This method is meant to serialise data from trusted user input,
551
- # like from your own database server or clients under your control, it could
552
- # be dangerous to allow untrusted users to pass JSON sources into it. The
553
- # default options for the parser can be changed via the load_default_options
554
- # method.
489
+ # ---
490
+ #
491
+ # When +proc+ is given:
492
+ # - Modifies +source+ as above.
493
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
494
+ # - Recursively calls <tt>proc(result)</tt>.
495
+ # - Returns the final result.
496
+ #
497
+ # Example:
498
+ # require 'json'
499
+ #
500
+ # # Some classes for the example.
501
+ # class Base
502
+ # def initialize(attributes)
503
+ # @attributes = attributes
504
+ # end
505
+ # end
506
+ # class User < Base; end
507
+ # class Account < Base; end
508
+ # class Admin < Base; end
509
+ # # The JSON source.
510
+ # json = <<-EOF
511
+ # {
512
+ # "users": [
513
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
514
+ # {"type": "User", "username": "john", "email": "john@example.com"}
515
+ # ],
516
+ # "accounts": [
517
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
518
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
519
+ # ],
520
+ # "admins": {"type": "Admin", "password": "0wn3d"}
521
+ # }
522
+ # EOF
523
+ # # Deserializer method.
524
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
525
+ # type = obj.is_a?(Hash) && obj["type"]
526
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
527
+ # end
528
+ # # Call to JSON.load
529
+ # ruby = JSON.load(json, proc {|obj|
530
+ # case obj
531
+ # when Hash
532
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
533
+ # when Array
534
+ # obj.map! {|v| deserialize_obj v }
535
+ # end
536
+ # })
537
+ # pp ruby
538
+ # Output:
539
+ # {"users"=>
540
+ # [#<User:0x00000000064c4c98
541
+ # @attributes=
542
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
543
+ # #<User:0x00000000064c4bd0
544
+ # @attributes=
545
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
546
+ # "accounts"=>
547
+ # [{"account"=>
548
+ # #<Account:0x00000000064c4928
549
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
550
+ # {"account"=>
551
+ # #<Account:0x00000000064c4680
552
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
553
+ # "admins"=>
554
+ # #<Admin:0x00000000064c41f8
555
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
555
556
  #
556
- # This method is part of the implementation of the load/dump interface of
557
- # Marshal and YAML.
558
557
  def load(source, proc = nil, options = {})
559
558
  opts = load_default_options.merge options
560
559
  if source.respond_to? :to_str
@@ -573,7 +572,7 @@ module JSON
573
572
  end
574
573
 
575
574
  # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
576
- def recurse_proc(result, &proc)
575
+ def recurse_proc(result, &proc) # :nodoc:
577
576
  case result
578
577
  when Array
579
578
  result.each { |x| recurse_proc x, &proc }
@@ -593,29 +592,42 @@ module JSON
593
592
  # Sets or returns the default options for the JSON.dump method.
594
593
  # Initially:
595
594
  # opts = JSON.dump_default_options
596
- # opts # => {:max_nesting=>false, :allow_nan=>true}
595
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
597
596
  attr_accessor :dump_default_options
598
597
  end
599
598
  self.dump_default_options = {
600
599
  :max_nesting => false,
601
600
  :allow_nan => true,
601
+ :escape_slash => false,
602
602
  }
603
603
 
604
- # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
605
- # the result.
604
+ # :call-seq:
605
+ # JSON.dump(obj, io = nil, limit = nil)
606
606
  #
607
- # If anIO (an IO-like object or an object that responds to the write method)
608
- # was given, the resulting JSON is written to it.
607
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
609
608
  #
610
- # If the number of nested arrays or objects exceeds _limit_, an ArgumentError
611
- # exception is raised. This argument is similar (but not exactly the
612
- # same!) to the _limit_ argument in Marshal.dump.
609
+ # The default options can be changed via method JSON.dump_default_options.
613
610
  #
614
- # The default options for the generator can be changed via the
615
- # dump_default_options method.
611
+ # - Argument +io+, if given, should respond to method +write+;
612
+ # the \JSON \String is written to +io+, and +io+ is returned.
613
+ # If +io+ is not given, the \JSON \String is returned.
614
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
616
615
  #
617
- # This method is part of the implementation of the load/dump interface of
618
- # Marshal and YAML.
616
+ # ---
617
+ #
618
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
619
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
620
+ # json = JSON.dump(obj)
621
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
622
+ #
623
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
624
+ # path = 't.json'
625
+ # File.open(path, 'w') do |file|
626
+ # JSON.dump(obj, file)
627
+ # end # => #<File:t.json (closed)>
628
+ # puts File.read(path)
629
+ # Output:
630
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
619
631
  def dump(obj, anIO = nil, limit = nil)
620
632
  if anIO and limit.nil?
621
633
  anIO = anIO.to_io if anIO.respond_to?(:to_io)