json 1.8.6-java → 2.11.1-java

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 (74) hide show
  1. checksums.yaml +5 -5
  2. data/BSDL +22 -0
  3. data/CHANGES.md +619 -0
  4. data/COPYING +56 -0
  5. data/LEGAL +8 -0
  6. data/README.md +268 -0
  7. data/json.gemspec +63 -0
  8. data/lib/json/add/bigdecimal.rb +41 -11
  9. data/lib/json/add/complex.rb +32 -9
  10. data/lib/json/add/core.rb +1 -0
  11. data/lib/json/add/date.rb +27 -7
  12. data/lib/json/add/date_time.rb +26 -9
  13. data/lib/json/add/exception.rb +25 -7
  14. data/lib/json/add/ostruct.rb +32 -9
  15. data/lib/json/add/range.rb +33 -8
  16. data/lib/json/add/rational.rb +30 -8
  17. data/lib/json/add/regexp.rb +28 -10
  18. data/lib/json/add/set.rb +48 -0
  19. data/lib/json/add/struct.rb +29 -7
  20. data/lib/json/add/symbol.rb +34 -7
  21. data/lib/json/add/time.rb +29 -15
  22. data/lib/json/common.rb +916 -316
  23. data/lib/json/ext/generator/state.rb +106 -0
  24. data/lib/json/ext/generator.jar +0 -0
  25. data/lib/json/ext/parser.jar +0 -0
  26. data/lib/json/ext.rb +34 -10
  27. data/lib/json/generic_object.rb +11 -6
  28. data/lib/json/truffle_ruby/generator.rb +690 -0
  29. data/lib/json/version.rb +3 -6
  30. data/lib/json.rb +560 -35
  31. metadata +29 -82
  32. data/lib/json/pure/generator.rb +0 -530
  33. data/lib/json/pure/parser.rb +0 -359
  34. data/lib/json/pure.rb +0 -21
  35. data/tests/fixtures/fail1.json +0 -1
  36. data/tests/fixtures/fail10.json +0 -1
  37. data/tests/fixtures/fail11.json +0 -1
  38. data/tests/fixtures/fail12.json +0 -1
  39. data/tests/fixtures/fail13.json +0 -1
  40. data/tests/fixtures/fail14.json +0 -1
  41. data/tests/fixtures/fail18.json +0 -1
  42. data/tests/fixtures/fail19.json +0 -1
  43. data/tests/fixtures/fail2.json +0 -1
  44. data/tests/fixtures/fail20.json +0 -1
  45. data/tests/fixtures/fail21.json +0 -1
  46. data/tests/fixtures/fail22.json +0 -1
  47. data/tests/fixtures/fail23.json +0 -1
  48. data/tests/fixtures/fail24.json +0 -1
  49. data/tests/fixtures/fail25.json +0 -1
  50. data/tests/fixtures/fail27.json +0 -2
  51. data/tests/fixtures/fail28.json +0 -2
  52. data/tests/fixtures/fail3.json +0 -1
  53. data/tests/fixtures/fail4.json +0 -1
  54. data/tests/fixtures/fail5.json +0 -1
  55. data/tests/fixtures/fail6.json +0 -1
  56. data/tests/fixtures/fail7.json +0 -1
  57. data/tests/fixtures/fail8.json +0 -1
  58. data/tests/fixtures/fail9.json +0 -1
  59. data/tests/fixtures/pass1.json +0 -56
  60. data/tests/fixtures/pass15.json +0 -1
  61. data/tests/fixtures/pass16.json +0 -1
  62. data/tests/fixtures/pass17.json +0 -1
  63. data/tests/fixtures/pass2.json +0 -1
  64. data/tests/fixtures/pass26.json +0 -1
  65. data/tests/fixtures/pass3.json +0 -6
  66. data/tests/setup_variant.rb +0 -11
  67. data/tests/test_json.rb +0 -519
  68. data/tests/test_json_addition.rb +0 -196
  69. data/tests/test_json_encoding.rb +0 -65
  70. data/tests/test_json_fixtures.rb +0 -35
  71. data/tests/test_json_generate.rb +0 -348
  72. data/tests/test_json_generic_object.rb +0 -75
  73. data/tests/test_json_string_matching.rb +0 -39
  74. data/tests/test_json_unicode.rb +0 -72
data/lib/json/common.rb CHANGED
@@ -1,30 +1,148 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json/version'
2
- require 'json/generic_object'
3
4
 
4
5
  module JSON
6
+ autoload :GenericObject, 'json/generic_object'
7
+
8
+ module ParserOptions # :nodoc:
9
+ class << self
10
+ def prepare(opts)
11
+ if opts[:object_class] || opts[:array_class]
12
+ opts = opts.dup
13
+ on_load = opts[:on_load]
14
+
15
+ on_load = object_class_proc(opts[:object_class], on_load) if opts[:object_class]
16
+ on_load = array_class_proc(opts[:array_class], on_load) if opts[:array_class]
17
+ opts[:on_load] = on_load
18
+ end
19
+
20
+ if opts.fetch(:create_additions, false) != false
21
+ opts = create_additions_proc(opts)
22
+ end
23
+
24
+ opts
25
+ end
26
+
27
+ private
28
+
29
+ def object_class_proc(object_class, on_load)
30
+ ->(obj) do
31
+ if Hash === obj
32
+ object = object_class.new
33
+ obj.each { |k, v| object[k] = v }
34
+ obj = object
35
+ end
36
+ on_load.nil? ? obj : on_load.call(obj)
37
+ end
38
+ end
39
+
40
+ def array_class_proc(array_class, on_load)
41
+ ->(obj) do
42
+ if Array === obj
43
+ array = array_class.new
44
+ obj.each { |v| array << v }
45
+ obj = array
46
+ end
47
+ on_load.nil? ? obj : on_load.call(obj)
48
+ end
49
+ end
50
+
51
+ # TODO: exctract :create_additions support to another gem for version 3.0
52
+ def create_additions_proc(opts)
53
+ if opts[:symbolize_names]
54
+ raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
55
+ end
56
+
57
+ opts = opts.dup
58
+ create_additions = opts.fetch(:create_additions, false)
59
+ on_load = opts[:on_load]
60
+ object_class = opts[:object_class] || Hash
61
+
62
+ opts[:on_load] = ->(object) do
63
+ case object
64
+ when String
65
+ opts[:match_string]&.each do |pattern, klass|
66
+ if match = pattern.match(object)
67
+ create_additions_warning if create_additions.nil?
68
+ object = klass.json_create(object)
69
+ break
70
+ end
71
+ end
72
+ when object_class
73
+ if opts[:create_additions] != false
74
+ if class_name = object[JSON.create_id]
75
+ klass = JSON.deep_const_get(class_name)
76
+ if (klass.respond_to?(:json_creatable?) && klass.json_creatable?) || klass.respond_to?(:json_create)
77
+ create_additions_warning if create_additions.nil?
78
+ object = klass.json_create(object)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ on_load.nil? ? object : on_load.call(object)
85
+ end
86
+
87
+ opts
88
+ end
89
+
90
+ GEM_ROOT = File.expand_path("../../../", __FILE__) + "/"
91
+ def create_additions_warning
92
+ message = "JSON.load implicit support for `create_additions: true` is deprecated " \
93
+ "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
94
+ "pass `create_additions: true`"
95
+
96
+ uplevel = 4
97
+ caller_locations(uplevel, 10).each do |frame|
98
+ if frame.path.nil? || frame.path.start_with?(GEM_ROOT) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
99
+ uplevel += 1
100
+ else
101
+ break
102
+ end
103
+ end
104
+
105
+ if RUBY_VERSION >= "3.0"
106
+ warn(message, uplevel: uplevel - 1, category: :deprecated)
107
+ else
108
+ warn(message, uplevel: uplevel - 1)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
5
114
  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.
115
+ # :call-seq:
116
+ # JSON[object] -> new_array or new_string
9
117
  #
10
- # The _opts_ argument is passed through to generate/parse respectively. See
11
- # generate and parse for their documentation.
12
- def [](object, opts = {})
13
- if object.respond_to? :to_str
14
- JSON.parse(object.to_str, opts)
15
- else
16
- JSON.generate(object, opts)
118
+ # If +object+ is a \String,
119
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
120
+ # json = '[0, 1, null]'
121
+ # JSON[json]# => [0, 1, nil]
122
+ #
123
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
124
+ # ruby = [0, 1, nil]
125
+ # JSON[ruby] # => '[0,1,null]'
126
+ def [](object, opts = nil)
127
+ if object.is_a?(String)
128
+ return JSON.parse(object, opts)
129
+ elsif object.respond_to?(:to_str)
130
+ str = object.to_str
131
+ if str.is_a?(String)
132
+ return JSON.parse(str, opts)
133
+ end
17
134
  end
135
+
136
+ JSON.generate(object, opts)
18
137
  end
19
138
 
20
- # Returns the JSON parser class that is used by JSON. This is either
21
- # JSON::Ext::Parser or JSON::Pure::Parser.
139
+ # Returns the JSON parser class that is used by JSON.
22
140
  attr_reader :parser
23
141
 
24
142
  # Set the JSON parser class _parser_ to be used by JSON.
25
143
  def parser=(parser) # :nodoc:
26
144
  @parser = parser
27
- remove_const :Parser if JSON.const_defined_in?(self, :Parser)
145
+ remove_const :Parser if const_defined?(:Parser, false)
28
146
  const_set :Parser, parser
29
147
  end
30
148
 
@@ -33,18 +151,9 @@ module JSON
33
151
  # level (absolute namespace path?). If there doesn't exist a constant at
34
152
  # the given path, an ArgumentError is raised.
35
153
  def deep_const_get(path) # :nodoc:
36
- path.to_s.split(/::/).inject(Object) do |p, c|
37
- case
38
- when c.empty? then p
39
- when JSON.const_defined_in?(p, c) then p.const_get(c)
40
- else
41
- begin
42
- p.const_missing(c)
43
- rescue NameError => e
44
- raise ArgumentError, "can't get const #{path}: #{e}"
45
- end
46
- end
47
- end
154
+ Object.const_get(path)
155
+ rescue NameError => e
156
+ raise ArgumentError, "can't get const #{path}: #{e}"
48
157
  end
49
158
 
50
159
  # Set the module _generator_ to be used by JSON.
@@ -53,7 +162,7 @@ module JSON
53
162
  @generator = generator
54
163
  generator_methods = generator::GeneratorMethods
55
164
  for const in generator_methods.constants
56
- klass = deep_const_get(const)
165
+ klass = const_get(const)
57
166
  modul = generator_methods.const_get(const)
58
167
  klass.class_eval do
59
168
  instance_methods(false).each do |m|
@@ -64,52 +173,61 @@ module JSON
64
173
  end
65
174
  self.state = generator::State
66
175
  const_set :State, self.state
67
- const_set :SAFE_STATE_PROTOTYPE, State.new
68
- const_set :FAST_STATE_PROTOTYPE, State.new(
69
- :indent => '',
70
- :space => '',
71
- :object_nl => "",
72
- :array_nl => "",
73
- :max_nesting => false
74
- )
75
- const_set :PRETTY_STATE_PROTOTYPE, State.new(
76
- :indent => ' ',
77
- :space => ' ',
78
- :object_nl => "\n",
79
- :array_nl => "\n"
80
- )
81
176
  ensure
82
177
  $VERBOSE = old
83
178
  end
84
179
 
85
- # Returns the JSON generator module that is used by JSON. This is
86
- # either JSON::Ext::Generator or JSON::Pure::Generator.
180
+ # Returns the JSON generator module that is used by JSON.
87
181
  attr_reader :generator
88
182
 
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.
183
+ # Sets or Returns the JSON generator state class that is used by JSON.
91
184
  attr_accessor :state
92
185
 
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
186
+ private
187
+
188
+ def deprecated_singleton_attr_accessor(*attrs)
189
+ args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
190
+ attrs.each do |attr|
191
+ singleton_class.class_eval <<~RUBY
192
+ def #{attr}
193
+ warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
194
+ @#{attr}
195
+ end
196
+
197
+ def #{attr}=(val)
198
+ warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
199
+ @#{attr} = val
200
+ end
201
+
202
+ def _#{attr}
203
+ @#{attr}
204
+ end
205
+ RUBY
206
+ end
207
+ end
208
+ end
209
+
210
+ # Sets create identifier, which is used to decide if the _json_create_
211
+ # hook of a class should be called; initial value is +json_class+:
212
+ # JSON.create_id # => 'json_class'
213
+ def self.create_id=(new_value)
214
+ Thread.current[:"JSON.create_id"] = new_value.dup.freeze
96
215
  end
97
- self.create_id = 'json_class'
98
216
 
99
- NaN = 0.0/0
217
+ # Returns the current create identifier.
218
+ # See also JSON.create_id=.
219
+ def self.create_id
220
+ Thread.current[:"JSON.create_id"] || 'json_class'
221
+ end
100
222
 
101
- Infinity = 1.0/0
223
+ NaN = Float::NAN
224
+
225
+ Infinity = Float::INFINITY
102
226
 
103
227
  MinusInfinity = -Infinity
104
228
 
105
229
  # The base exception for JSON errors.
106
- class JSONError < StandardError
107
- def self.wrap(exception)
108
- obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
109
- obj.set_backtrace exception.backtrace
110
- obj
111
- end
112
- end
230
+ class JSONError < StandardError; end
113
231
 
114
232
  # This exception is raised if a parser error occurs.
115
233
  class ParserError < JSONError; end
@@ -118,217 +236,449 @@ module JSON
118
236
  # deep.
119
237
  class NestingError < ParserError; end
120
238
 
121
- # :stopdoc:
122
- class CircularDatastructure < NestingError; end
123
- # :startdoc:
124
-
125
239
  # This exception is raised if a generator or unparser error occurs.
126
- class GeneratorError < JSONError; end
127
- # For backwards compatibility
128
- UnparserError = GeneratorError
240
+ class GeneratorError < JSONError
241
+ attr_reader :invalid_object
129
242
 
130
- # This exception is raised if the required unicode support is missing on the
131
- # system. Usually this means that the iconv library is not installed.
132
- class MissingUnicodeSupport < JSONError; end
243
+ def initialize(message, invalid_object = nil)
244
+ super(message)
245
+ @invalid_object = invalid_object
246
+ end
247
+
248
+ def detailed_message(...)
249
+ # Exception#detailed_message doesn't exist until Ruby 3.2
250
+ super_message = defined?(super) ? super : message
251
+
252
+ if @invalid_object.nil?
253
+ super_message
254
+ else
255
+ "#{super_message}\nInvalid object: #{@invalid_object.inspect}"
256
+ end
257
+ end
258
+ end
259
+
260
+ # Fragment of JSON document that is to be included as is:
261
+ # fragment = JSON::Fragment.new("[1, 2, 3]")
262
+ # JSON.generate({ count: 3, items: fragments })
263
+ #
264
+ # This allows to easily assemble multiple JSON fragments that have
265
+ # been persisted somewhere without having to parse them nor resorting
266
+ # to string interpolation.
267
+ #
268
+ # Note: no validation is performed on the provided string. It is the
269
+ # responsability of the caller to ensure the string contains valid JSON.
270
+ Fragment = Struct.new(:json) do
271
+ def initialize(json)
272
+ unless string = String.try_convert(json)
273
+ raise TypeError, " no implicit conversion of #{json.class} into String"
274
+ end
275
+
276
+ super(string)
277
+ end
278
+
279
+ def to_json(state = nil, *)
280
+ json
281
+ end
282
+ end
133
283
 
134
284
  module_function
135
285
 
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
154
- def parse(source, opts = {})
155
- Parser.new(source, opts).parse
286
+ # :call-seq:
287
+ # JSON.parse(source, opts) -> object
288
+ #
289
+ # Returns the Ruby objects created by parsing the given +source+.
290
+ #
291
+ # Argument +source+ contains the \String to be parsed.
292
+ #
293
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
294
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
295
+ #
296
+ # ---
297
+ #
298
+ # When +source+ is a \JSON array, returns a Ruby \Array:
299
+ # source = '["foo", 1.0, true, false, null]'
300
+ # ruby = JSON.parse(source)
301
+ # ruby # => ["foo", 1.0, true, false, nil]
302
+ # ruby.class # => Array
303
+ #
304
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
305
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
306
+ # ruby = JSON.parse(source)
307
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
308
+ # ruby.class # => Hash
309
+ #
310
+ # For examples of parsing for all \JSON data types, see
311
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
312
+ #
313
+ # Parses nested JSON objects:
314
+ # source = <<~JSON
315
+ # {
316
+ # "name": "Dave",
317
+ # "age" :40,
318
+ # "hats": [
319
+ # "Cattleman's",
320
+ # "Panama",
321
+ # "Tophat"
322
+ # ]
323
+ # }
324
+ # JSON
325
+ # ruby = JSON.parse(source)
326
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
327
+ #
328
+ # ---
329
+ #
330
+ # Raises an exception if +source+ is not valid JSON:
331
+ # # Raises JSON::ParserError (783: unexpected token at ''):
332
+ # JSON.parse('')
333
+ #
334
+ def parse(source, opts = nil)
335
+ opts = ParserOptions.prepare(opts) unless opts.nil?
336
+ Parser.parse(source, opts)
337
+ end
338
+
339
+ PARSE_L_OPTIONS = {
340
+ max_nesting: false,
341
+ allow_nan: true,
342
+ }.freeze
343
+ private_constant :PARSE_L_OPTIONS
344
+
345
+ # :call-seq:
346
+ # JSON.parse!(source, opts) -> object
347
+ #
348
+ # Calls
349
+ # parse(source, opts)
350
+ # with +source+ and possibly modified +opts+.
351
+ #
352
+ # Differences from JSON.parse:
353
+ # - Option +max_nesting+, if not provided, defaults to +false+,
354
+ # which disables checking for nesting depth.
355
+ # - Option +allow_nan+, if not provided, defaults to +true+.
356
+ def parse!(source, opts = nil)
357
+ if opts.nil?
358
+ parse(source, PARSE_L_OPTIONS)
359
+ else
360
+ parse(source, PARSE_L_OPTIONS.merge(opts))
361
+ end
362
+ end
363
+
364
+ # :call-seq:
365
+ # JSON.load_file(path, opts={}) -> object
366
+ #
367
+ # Calls:
368
+ # parse(File.read(path), opts)
369
+ #
370
+ # See method #parse.
371
+ def load_file(filespec, opts = nil)
372
+ parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
156
373
  end
157
374
 
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.
173
- def parse!(source, opts = {})
174
- opts = {
175
- :max_nesting => false,
176
- :allow_nan => true
177
- }.update(opts)
178
- Parser.new(source, opts).parse
375
+ # :call-seq:
376
+ # JSON.load_file!(path, opts = {})
377
+ #
378
+ # Calls:
379
+ # JSON.parse!(File.read(path, opts))
380
+ #
381
+ # See method #parse!
382
+ def load_file!(filespec, opts = nil)
383
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
179
384
  end
180
385
 
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.
386
+ # :call-seq:
387
+ # JSON.generate(obj, opts = nil) -> new_string
388
+ #
389
+ # Returns a \String containing the generated \JSON data.
390
+ #
391
+ # See also JSON.fast_generate, JSON.pretty_generate.
392
+ #
393
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
394
+ #
395
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
396
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
397
+ #
398
+ # ---
399
+ #
400
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
401
+ # obj = ["foo", 1.0, true, false, nil]
402
+ # json = JSON.generate(obj)
403
+ # json # => '["foo",1.0,true,false,null]'
404
+ #
405
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
406
+ # obj = {foo: 0, bar: 's', baz: :bat}
407
+ # json = JSON.generate(obj)
408
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
409
+ #
410
+ # For examples of generating from other Ruby objects, see
411
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
412
+ #
413
+ # ---
414
+ #
415
+ # Raises an exception if any formatting option is not a \String.
416
+ #
417
+ # Raises an exception if +obj+ contains circular references:
418
+ # a = []; b = []; a.push(b); b.push(a)
419
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
420
+ # JSON.generate(a)
421
+ #
207
422
  def generate(obj, opts = nil)
208
423
  if State === opts
209
- state, opts = opts, nil
424
+ opts.generate(obj)
210
425
  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)
426
+ State.generate(obj, opts, nil)
222
427
  end
223
- state.generate(obj)
224
428
  end
225
429
 
226
- # :stopdoc:
227
- # I want to deprecate these later, so I'll first be silent about them, and
228
- # later delete them.
229
- alias unparse generate
230
- module_function :unparse
231
- # :startdoc:
232
-
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.
430
+ # :call-seq:
431
+ # JSON.fast_generate(obj, opts) -> new_string
235
432
  #
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.
433
+ # Arguments +obj+ and +opts+ here are the same as
434
+ # arguments +obj+ and +opts+ in JSON.generate.
435
+ #
436
+ # By default, generates \JSON data without checking
437
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
438
+ #
439
+ # Raises an exception if +obj+ contains circular references:
440
+ # a = []; b = []; a.push(b); b.push(a)
441
+ # # Raises SystemStackError (stack level too deep):
442
+ # JSON.fast_generate(a)
238
443
  def fast_generate(obj, opts = nil)
239
- if State === opts
240
- state, opts = opts, nil
444
+ if RUBY_VERSION >= "3.0"
445
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
241
446
  else
242
- state = FAST_STATE_PROTOTYPE.dup
447
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
243
448
  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)
253
- end
254
- state.generate(obj)
449
+ generate(obj, opts)
255
450
  end
256
451
 
257
- # :stopdoc:
258
- # I want to deprecate these later, so I'll first be silent about them, and later delete them.
259
- alias fast_unparse fast_generate
260
- module_function :fast_unparse
261
- # :startdoc:
452
+ PRETTY_GENERATE_OPTIONS = {
453
+ indent: ' ',
454
+ space: ' ',
455
+ object_nl: "\n",
456
+ array_nl: "\n",
457
+ }.freeze
458
+ private_constant :PRETTY_GENERATE_OPTIONS
262
459
 
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.
460
+ # :call-seq:
461
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
462
+ #
463
+ # Arguments +obj+ and +opts+ here are the same as
464
+ # arguments +obj+ and +opts+ in JSON.generate.
465
+ #
466
+ # Default options are:
467
+ # {
468
+ # indent: ' ', # Two spaces
469
+ # space: ' ', # One space
470
+ # array_nl: "\n", # Newline
471
+ # object_nl: "\n" # Newline
472
+ # }
473
+ #
474
+ # Example:
475
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
476
+ # json = JSON.pretty_generate(obj)
477
+ # puts json
478
+ # Output:
479
+ # {
480
+ # "foo": [
481
+ # "bar",
482
+ # "baz"
483
+ # ],
484
+ # "bat": {
485
+ # "bam": 0,
486
+ # "bad": 1
487
+ # }
488
+ # }
266
489
  #
267
- # The _opts_ argument can be used to configure the generator. See the
268
- # generate method for a more detailed explanation.
269
490
  def pretty_generate(obj, opts = nil)
270
- if State === opts
271
- state, opts = opts, nil
272
- else
273
- state = PRETTY_STATE_PROTOTYPE.dup
274
- end
491
+ return state.generate(obj) if State === opts
492
+
493
+ options = PRETTY_GENERATE_OPTIONS
494
+
275
495
  if opts
276
- if opts.respond_to? :to_hash
277
- opts = opts.to_hash
278
- elsif opts.respond_to? :to_h
279
- opts = opts.to_h
280
- else
281
- raise TypeError, "can't convert #{opts.class} into Hash"
496
+ unless opts.is_a?(Hash)
497
+ if opts.respond_to? :to_hash
498
+ opts = opts.to_hash
499
+ elsif opts.respond_to? :to_h
500
+ opts = opts.to_h
501
+ else
502
+ raise TypeError, "can't convert #{opts.class} into Hash"
503
+ end
282
504
  end
283
- state.configure(opts)
505
+ options = options.merge(opts)
284
506
  end
285
- state.generate(obj)
507
+
508
+ State.generate(obj, options, nil)
286
509
  end
287
510
 
288
- # :stopdoc:
289
- # I want to deprecate these later, so I'll first be silent about them, and later delete them.
290
- alias pretty_unparse pretty_generate
291
- module_function :pretty_unparse
292
- # :startdoc:
511
+ # Sets or returns default options for the JSON.unsafe_load method.
512
+ # Initially:
513
+ # opts = JSON.load_default_options
514
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
515
+ deprecated_singleton_attr_accessor :unsafe_load_default_options
293
516
 
294
- class << self
295
- # The global default options for the JSON.load method:
296
- # :max_nesting: false
297
- # :allow_nan: true
298
- # :quirks_mode: true
299
- attr_accessor :load_default_options
300
- end
301
- self.load_default_options = {
517
+ @unsafe_load_default_options = {
302
518
  :max_nesting => false,
303
519
  :allow_nan => true,
304
- :quirks_mode => true,
520
+ :allow_blank => true,
305
521
  :create_additions => true,
306
522
  }
307
523
 
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.
524
+ # Sets or returns default options for the JSON.load method.
525
+ # Initially:
526
+ # opts = JSON.load_default_options
527
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
528
+ deprecated_singleton_attr_accessor :load_default_options
529
+
530
+ @load_default_options = {
531
+ :allow_nan => true,
532
+ :allow_blank => true,
533
+ :create_additions => nil,
534
+ }
535
+ # :call-seq:
536
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
537
+ #
538
+ # Returns the Ruby objects created by parsing the given +source+.
313
539
  #
314
540
  # BEWARE: This method is meant to serialise data from trusted user input,
315
541
  # 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.
319
- #
320
- # This method is part of the implementation of the load/dump interface of
321
- # Marshal and YAML.
322
- def load(source, proc = nil, options = {})
323
- opts = load_default_options.merge options
324
- if source.respond_to? :to_str
325
- source = source.to_str
326
- elsif source.respond_to? :to_io
327
- source = source.to_io.read
328
- elsif source.respond_to?(:read)
329
- source = source.read
330
- end
331
- if opts[:quirks_mode] && (source.nil? || source.empty?)
542
+ # be dangerous to allow untrusted users to pass JSON sources into it.
543
+ #
544
+ # - Argument +source+ must be, or be convertible to, a \String:
545
+ # - If +source+ responds to instance method +to_str+,
546
+ # <tt>source.to_str</tt> becomes the source.
547
+ # - If +source+ responds to instance method +to_io+,
548
+ # <tt>source.to_io.read</tt> becomes the source.
549
+ # - If +source+ responds to instance method +read+,
550
+ # <tt>source.read</tt> becomes the source.
551
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
552
+ # - Option +allow_blank+ specifies a truthy value.
553
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
554
+ # - Otherwise, +source+ remains the source.
555
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
556
+ # It will be called recursively with each result (depth-first order).
557
+ # See details below.
558
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
559
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
560
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
561
+ #
562
+ # ---
563
+ #
564
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
565
+ # <tt>parse(source, opts)</tt>; see #parse.
566
+ #
567
+ # Source for following examples:
568
+ # source = <<~JSON
569
+ # {
570
+ # "name": "Dave",
571
+ # "age" :40,
572
+ # "hats": [
573
+ # "Cattleman's",
574
+ # "Panama",
575
+ # "Tophat"
576
+ # ]
577
+ # }
578
+ # JSON
579
+ #
580
+ # Load a \String:
581
+ # ruby = JSON.unsafe_load(source)
582
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
583
+ #
584
+ # Load an \IO object:
585
+ # require 'stringio'
586
+ # object = JSON.unsafe_load(StringIO.new(source))
587
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
588
+ #
589
+ # Load a \File object:
590
+ # path = 't.json'
591
+ # File.write(path, source)
592
+ # File.open(path) do |file|
593
+ # JSON.unsafe_load(file)
594
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
595
+ #
596
+ # ---
597
+ #
598
+ # When +proc+ is given:
599
+ # - Modifies +source+ as above.
600
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
601
+ # - Recursively calls <tt>proc(result)</tt>.
602
+ # - Returns the final result.
603
+ #
604
+ # Example:
605
+ # require 'json'
606
+ #
607
+ # # Some classes for the example.
608
+ # class Base
609
+ # def initialize(attributes)
610
+ # @attributes = attributes
611
+ # end
612
+ # end
613
+ # class User < Base; end
614
+ # class Account < Base; end
615
+ # class Admin < Base; end
616
+ # # The JSON source.
617
+ # json = <<-EOF
618
+ # {
619
+ # "users": [
620
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
621
+ # {"type": "User", "username": "john", "email": "john@example.com"}
622
+ # ],
623
+ # "accounts": [
624
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
625
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
626
+ # ],
627
+ # "admins": {"type": "Admin", "password": "0wn3d"}
628
+ # }
629
+ # EOF
630
+ # # Deserializer method.
631
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
632
+ # type = obj.is_a?(Hash) && obj["type"]
633
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
634
+ # end
635
+ # # Call to JSON.unsafe_load
636
+ # ruby = JSON.unsafe_load(json, proc {|obj|
637
+ # case obj
638
+ # when Hash
639
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
640
+ # when Array
641
+ # obj.map! {|v| deserialize_obj v }
642
+ # end
643
+ # })
644
+ # pp ruby
645
+ # Output:
646
+ # {"users"=>
647
+ # [#<User:0x00000000064c4c98
648
+ # @attributes=
649
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
650
+ # #<User:0x00000000064c4bd0
651
+ # @attributes=
652
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
653
+ # "accounts"=>
654
+ # [{"account"=>
655
+ # #<Account:0x00000000064c4928
656
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
657
+ # {"account"=>
658
+ # #<Account:0x00000000064c4680
659
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
660
+ # "admins"=>
661
+ # #<Admin:0x00000000064c41f8
662
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
663
+ #
664
+ def unsafe_load(source, proc = nil, options = nil)
665
+ opts = if options.nil?
666
+ _unsafe_load_default_options
667
+ else
668
+ _unsafe_load_default_options.merge(options)
669
+ end
670
+
671
+ unless source.is_a?(String)
672
+ if source.respond_to? :to_str
673
+ source = source.to_str
674
+ elsif source.respond_to? :to_io
675
+ source = source.to_io.read
676
+ elsif source.respond_to?(:read)
677
+ source = source.read
678
+ end
679
+ end
680
+
681
+ if opts[:allow_blank] && (source.nil? || source.empty?)
332
682
  source = 'null'
333
683
  end
334
684
  result = parse(source, opts)
@@ -336,102 +686,355 @@ module JSON
336
686
  result
337
687
  end
338
688
 
339
- # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
340
- def recurse_proc(result, &proc)
341
- case result
342
- when Array
343
- result.each { |x| recurse_proc x, &proc }
344
- proc.call result
345
- when Hash
346
- result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
347
- proc.call result
689
+ # :call-seq:
690
+ # JSON.load(source, proc = nil, options = {}) -> object
691
+ #
692
+ # Returns the Ruby objects created by parsing the given +source+.
693
+ #
694
+ # BEWARE: This method is meant to serialise data from trusted user input,
695
+ # like from your own database server or clients under your control, it could
696
+ # be dangerous to allow untrusted users to pass JSON sources into it.
697
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
698
+ #
699
+ # Since JSON version 2.8.0, `load` emits a deprecation warning when a
700
+ # non native type is deserialized, without `create_additions` being explicitly
701
+ # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
702
+ # by default.
703
+ #
704
+ # - Argument +source+ must be, or be convertible to, a \String:
705
+ # - If +source+ responds to instance method +to_str+,
706
+ # <tt>source.to_str</tt> becomes the source.
707
+ # - If +source+ responds to instance method +to_io+,
708
+ # <tt>source.to_io.read</tt> becomes the source.
709
+ # - If +source+ responds to instance method +read+,
710
+ # <tt>source.read</tt> becomes the source.
711
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
712
+ # - Option +allow_blank+ specifies a truthy value.
713
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
714
+ # - Otherwise, +source+ remains the source.
715
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
716
+ # It will be called recursively with each result (depth-first order).
717
+ # See details below.
718
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
719
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
720
+ # The default options can be changed via method JSON.load_default_options=.
721
+ #
722
+ # ---
723
+ #
724
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
725
+ # <tt>parse(source, opts)</tt>; see #parse.
726
+ #
727
+ # Source for following examples:
728
+ # source = <<~JSON
729
+ # {
730
+ # "name": "Dave",
731
+ # "age" :40,
732
+ # "hats": [
733
+ # "Cattleman's",
734
+ # "Panama",
735
+ # "Tophat"
736
+ # ]
737
+ # }
738
+ # JSON
739
+ #
740
+ # Load a \String:
741
+ # ruby = JSON.load(source)
742
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
743
+ #
744
+ # Load an \IO object:
745
+ # require 'stringio'
746
+ # object = JSON.load(StringIO.new(source))
747
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
748
+ #
749
+ # Load a \File object:
750
+ # path = 't.json'
751
+ # File.write(path, source)
752
+ # File.open(path) do |file|
753
+ # JSON.load(file)
754
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
755
+ #
756
+ # ---
757
+ #
758
+ # When +proc+ is given:
759
+ # - Modifies +source+ as above.
760
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
761
+ # - Recursively calls <tt>proc(result)</tt>.
762
+ # - Returns the final result.
763
+ #
764
+ # Example:
765
+ # require 'json'
766
+ #
767
+ # # Some classes for the example.
768
+ # class Base
769
+ # def initialize(attributes)
770
+ # @attributes = attributes
771
+ # end
772
+ # end
773
+ # class User < Base; end
774
+ # class Account < Base; end
775
+ # class Admin < Base; end
776
+ # # The JSON source.
777
+ # json = <<-EOF
778
+ # {
779
+ # "users": [
780
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
781
+ # {"type": "User", "username": "john", "email": "john@example.com"}
782
+ # ],
783
+ # "accounts": [
784
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
785
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
786
+ # ],
787
+ # "admins": {"type": "Admin", "password": "0wn3d"}
788
+ # }
789
+ # EOF
790
+ # # Deserializer method.
791
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
792
+ # type = obj.is_a?(Hash) && obj["type"]
793
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
794
+ # end
795
+ # # Call to JSON.load
796
+ # ruby = JSON.load(json, proc {|obj|
797
+ # case obj
798
+ # when Hash
799
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
800
+ # when Array
801
+ # obj.map! {|v| deserialize_obj v }
802
+ # end
803
+ # })
804
+ # pp ruby
805
+ # Output:
806
+ # {"users"=>
807
+ # [#<User:0x00000000064c4c98
808
+ # @attributes=
809
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
810
+ # #<User:0x00000000064c4bd0
811
+ # @attributes=
812
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
813
+ # "accounts"=>
814
+ # [{"account"=>
815
+ # #<Account:0x00000000064c4928
816
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
817
+ # {"account"=>
818
+ # #<Account:0x00000000064c4680
819
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
820
+ # "admins"=>
821
+ # #<Admin:0x00000000064c41f8
822
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
823
+ #
824
+ def load(source, proc = nil, options = nil)
825
+ opts = if options.nil?
826
+ _load_default_options
348
827
  else
349
- proc.call result
828
+ _load_default_options.merge(options)
350
829
  end
351
- end
352
830
 
353
- alias restore load
354
- module_function :restore
831
+ unless source.is_a?(String)
832
+ if source.respond_to? :to_str
833
+ source = source.to_str
834
+ elsif source.respond_to? :to_io
835
+ source = source.to_io.read
836
+ elsif source.respond_to?(:read)
837
+ source = source.read
838
+ end
839
+ end
355
840
 
356
- class << self
357
- # The global default options for the JSON.dump method:
358
- # :max_nesting: false
359
- # :allow_nan: true
360
- # :quirks_mode: true
361
- attr_accessor :dump_default_options
841
+ if opts[:allow_blank] && (source.nil? || source.empty?)
842
+ source = 'null'
843
+ end
844
+
845
+ if proc
846
+ opts = opts.dup
847
+ opts[:on_load] = proc.to_proc
848
+ end
849
+
850
+ parse(source, opts)
362
851
  end
363
- self.dump_default_options = {
852
+
853
+ # Sets or returns the default options for the JSON.dump method.
854
+ # Initially:
855
+ # opts = JSON.dump_default_options
856
+ # opts # => {:max_nesting=>false, :allow_nan=>true}
857
+ deprecated_singleton_attr_accessor :dump_default_options
858
+ @dump_default_options = {
364
859
  :max_nesting => false,
365
860
  :allow_nan => true,
366
- :quirks_mode => true,
367
861
  }
368
862
 
369
- # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
370
- # the result.
863
+ # :call-seq:
864
+ # JSON.dump(obj, io = nil, limit = nil)
865
+ #
866
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
867
+ #
868
+ # The default options can be changed via method JSON.dump_default_options.
371
869
  #
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.
870
+ # - Argument +io+, if given, should respond to method +write+;
871
+ # the \JSON \String is written to +io+, and +io+ is returned.
872
+ # If +io+ is not given, the \JSON \String is returned.
873
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
374
874
  #
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.
875
+ # ---
378
876
  #
379
- # The default options for the generator can be changed via the
380
- # dump_default_options method.
877
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
878
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
879
+ # json = JSON.dump(obj)
880
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
381
881
  #
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
882
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
883
+ # path = 't.json'
884
+ # File.open(path, 'w') do |file|
885
+ # JSON.dump(obj, file)
886
+ # end # => #<File:t.json (closed)>
887
+ # puts File.read(path)
888
+ # Output:
889
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
890
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
891
+ if kwargs.nil?
892
+ if limit.nil?
893
+ if anIO.is_a?(Hash)
894
+ kwargs = anIO
895
+ anIO = nil
896
+ end
897
+ elsif limit.is_a?(Hash)
898
+ kwargs = limit
899
+ limit = nil
900
+ end
901
+ end
902
+
903
+ unless anIO.nil?
904
+ if anIO.respond_to?(:to_io)
905
+ anIO = anIO.to_io
906
+ elsif limit.nil? && !anIO.respond_to?(:write)
907
+ anIO, limit = nil, anIO
390
908
  end
391
909
  end
392
- opts = JSON.dump_default_options
910
+
911
+ opts = JSON._dump_default_options
393
912
  opts = opts.merge(:max_nesting => limit) if limit
394
- result = generate(obj, opts)
395
- if anIO
396
- anIO.write result
397
- anIO
913
+ opts = opts.merge(kwargs) if kwargs
914
+
915
+ begin
916
+ State.generate(obj, opts, anIO)
917
+ rescue JSON::NestingError
918
+ raise ArgumentError, "exceed depth limit"
919
+ end
920
+ end
921
+
922
+ # :stopdoc:
923
+ # All these were meant to be deprecated circa 2009, but were just set as undocumented
924
+ # so usage still exist in the wild.
925
+ def unparse(...)
926
+ if RUBY_VERSION >= "3.0"
927
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
398
928
  else
399
- result
929
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
400
930
  end
401
- rescue JSON::NestingError
402
- raise ArgumentError, "exceed depth limit"
931
+ generate(...)
403
932
  end
933
+ module_function :unparse
404
934
 
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]
935
+ def fast_unparse(...)
936
+ if RUBY_VERSION >= "3.0"
937
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
938
+ else
939
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
410
940
  end
411
- string
941
+ generate(...)
412
942
  end
943
+ module_function :fast_unparse
413
944
 
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)
945
+ def pretty_unparse(...)
946
+ if RUBY_VERSION >= "3.0"
947
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
948
+ else
949
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
419
950
  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)
951
+ pretty_generate(...)
952
+ end
953
+ module_function :fast_unparse
954
+
955
+ def restore(...)
956
+ if RUBY_VERSION >= "3.0"
957
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
958
+ else
959
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
425
960
  end
961
+ load(...)
426
962
  end
963
+ module_function :restore
964
+ # :startdoc:
965
+
966
+ # JSON::Coder holds a parser and generator configuration.
967
+ #
968
+ # module MyApp
969
+ # JSONC_CODER = JSON::Coder.new(
970
+ # allow_trailing_comma: true
971
+ # )
972
+ # end
973
+ #
974
+ # MyApp::JSONC_CODER.load(document)
975
+ #
976
+ class Coder
977
+ # :call-seq:
978
+ # JSON.new(options = nil, &block)
979
+ #
980
+ # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
981
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
982
+ #
983
+ # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
984
+ # encoutered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
985
+ # \JSON counterpart:
986
+ #
987
+ # module MyApp
988
+ # API_JSON_CODER = JSON::Coder.new do |object|
989
+ # case object
990
+ # when Time
991
+ # object.iso8601(3)
992
+ # else
993
+ # object # Unknown type, will raise
994
+ # end
995
+ # end
996
+ # end
997
+ #
998
+ # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
999
+ #
1000
+ def initialize(options = nil, &as_json)
1001
+ if options.nil?
1002
+ options = { strict: true }
1003
+ else
1004
+ options = options.dup
1005
+ options[:strict] = true
1006
+ end
1007
+ options[:as_json] = as_json if as_json
427
1008
 
428
- if ::Object.method(:const_defined?).arity == 1
429
- def self.const_defined_in?(modul, constant)
430
- modul.const_defined?(constant)
1009
+ @state = State.new(options).freeze
1010
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
431
1011
  end
432
- else
433
- def self.const_defined_in?(modul, constant)
434
- modul.const_defined?(constant, false)
1012
+
1013
+ # call-seq:
1014
+ # dump(object) -> String
1015
+ # dump(object, io) -> io
1016
+ #
1017
+ # Serialize the given object into a \JSON document.
1018
+ def dump(object, io = nil)
1019
+ @state.generate_new(object, io)
1020
+ end
1021
+ alias_method :generate, :dump
1022
+
1023
+ # call-seq:
1024
+ # load(string) -> Object
1025
+ #
1026
+ # Parse the given \JSON document and return an equivalent Ruby object.
1027
+ def load(source)
1028
+ @parser_config.parse(source)
1029
+ end
1030
+ alias_method :parse, :load
1031
+
1032
+ # call-seq:
1033
+ # load(path) -> Object
1034
+ #
1035
+ # Parse the given \JSON document and return an equivalent Ruby object.
1036
+ def load_file(path)
1037
+ load(File.read(path, encoding: Encoding::UTF_8))
435
1038
  end
436
1039
  end
437
1040
  end
@@ -442,6 +1045,12 @@ module ::Kernel
442
1045
  # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
443
1046
  # one line.
444
1047
  def j(*objs)
1048
+ if RUBY_VERSION >= "3.0"
1049
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1050
+ else
1051
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1
1052
+ end
1053
+
445
1054
  objs.each do |obj|
446
1055
  puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
447
1056
  end
@@ -451,6 +1060,12 @@ module ::Kernel
451
1060
  # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
452
1061
  # indentation and over many lines.
453
1062
  def jj(*objs)
1063
+ if RUBY_VERSION >= "3.0"
1064
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1065
+ else
1066
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
1067
+ end
1068
+
454
1069
  objs.each do |obj|
455
1070
  puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
456
1071
  end
@@ -463,22 +1078,7 @@ module ::Kernel
463
1078
  #
464
1079
  # The _opts_ argument is passed through to generate/parse respectively. See
465
1080
  # generate and parse for their documentation.
466
- def JSON(object, *args)
467
- if object.respond_to? :to_str
468
- JSON.parse(object.to_str, args.first)
469
- else
470
- JSON.generate(object, args.first)
471
- end
472
- end
473
- end
474
-
475
- # Extends any Class to include _json_creatable?_ method.
476
- class ::Class
477
- # Returns true if this class can be used to create an instance
478
- # from a serialised JSON string. The class has to implement a class
479
- # method _json_create_ that expects a hash as first parameter. The hash
480
- # should include the required data.
481
- def json_creatable?
482
- respond_to?(:json_create)
1081
+ def JSON(object, opts = nil)
1082
+ JSON[object, opts]
483
1083
  end
484
1084
  end