json 2.21.2 → 3.1.0

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.
data/lib/json/common.rb CHANGED
@@ -3,25 +3,12 @@
3
3
  require 'json/version'
4
4
 
5
5
  module JSON
6
- autoload :GenericObject, 'json/generic_object'
7
-
8
6
  module ParserOptions # :nodoc:
9
7
  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
8
+ def on_load(on_load, object_class, array_class)
9
+ on_load = object_class_proc(object_class, on_load) if object_class
10
+ on_load = array_class_proc(array_class, on_load) if array_class
11
+ on_load
25
12
  end
26
13
 
27
14
  private
@@ -47,77 +34,12 @@ module JSON
47
34
  on_load.nil? ? obj : on_load.call(obj)
48
35
  end
49
36
  end
50
-
51
- # TODO: extract :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_path = object[JSON.create_id]
75
- klass = begin
76
- Object.const_get(class_path)
77
- rescue NameError => e
78
- raise ArgumentError, "can't get const #{class_path}: #{e}"
79
- end
80
-
81
- if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create)
82
- create_additions_warning if create_additions.nil?
83
- object = klass.json_create(object)
84
- end
85
- end
86
- end
87
- end
88
-
89
- on_load.nil? ? object : on_load.call(object)
90
- end
91
-
92
- opts
93
- end
94
-
95
- def create_additions_warning
96
- JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
97
- "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
98
- "pass `create_additions: true`"
99
- end
100
37
  end
101
38
  end
102
39
 
103
- class << self
104
- def deprecation_warning(message, uplevel = 3) # :nodoc:
105
- gem_root = File.expand_path("..", __dir__) + "/"
106
- caller_locations(uplevel, 10).each do |frame|
107
- if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
108
- uplevel += 1
109
- else
110
- break
111
- end
112
- end
113
-
114
- if RUBY_VERSION >= "3.0"
115
- warn(message, uplevel: uplevel, category: :deprecated)
116
- else
117
- warn(message, uplevel: uplevel)
118
- end
119
- end
40
+ private_constant :ParserOptions
120
41
 
42
+ class << self
121
43
  # :call-seq:
122
44
  # JSON[object] -> new_array or new_string
123
45
  #
@@ -130,12 +52,14 @@ module JSON
130
52
  # ruby = [0, 1, nil]
131
53
  # JSON[ruby] # => '[0,1,null]'
132
54
  def [](object, opts = nil)
55
+ opts ||= {}.freeze
56
+
133
57
  if object.is_a?(String)
134
- return JSON.parse(object, opts)
58
+ return JSON.parse(object, **opts)
135
59
  elsif object.respond_to?(:to_str)
136
60
  str = object.to_str
137
61
  if str.is_a?(String)
138
- return JSON.parse(str, opts)
62
+ return JSON.parse(str, **opts)
139
63
  end
140
64
  end
141
65
 
@@ -156,14 +80,6 @@ module JSON
156
80
  def generator=(generator) # :nodoc:
157
81
  old, $VERBOSE = $VERBOSE, nil
158
82
 
159
- # The default proc used when the +sort_keys+ generation option is +true+.
160
- # It returns a new hash with the entries sorted by their keys.
161
- sort_keys_proc = ->(hash) { hash.sort.to_h }
162
- if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
163
- sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
164
- end
165
- generator::State.default_sort_keys_proc = sort_keys_proc
166
-
167
83
  @generator = generator
168
84
  if generator.const_defined?(:GeneratorMethods)
169
85
  generator_methods = generator::GeneratorMethods
@@ -193,57 +109,18 @@ module JSON
193
109
  private
194
110
 
195
111
  # Called from the extension when a hash has both string and symbol keys
196
- def on_mixed_keys_hash(hash, do_raise)
112
+ def on_mixed_keys_hash(hash)
197
113
  set = {}
198
114
  hash.each_key do |key|
199
115
  key_str = key.to_s
200
116
 
201
117
  if set[key_str]
202
- message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
203
- if do_raise
204
- raise GeneratorError, message
205
- else
206
- deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
207
- end
118
+ raise GeneratorError, "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
208
119
  else
209
120
  set[key_str] = true
210
121
  end
211
122
  end
212
123
  end
213
-
214
- def deprecated_singleton_attr_accessor(*attrs)
215
- args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
216
- attrs.each do |attr|
217
- singleton_class.class_eval <<~RUBY
218
- def #{attr}
219
- warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
220
- @#{attr}
221
- end
222
-
223
- def #{attr}=(val)
224
- warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
225
- @#{attr} = val
226
- end
227
-
228
- def _#{attr}
229
- @#{attr}
230
- end
231
- RUBY
232
- end
233
- end
234
- end
235
-
236
- # Sets create identifier, which is used to decide if the _json_create_
237
- # hook of a class should be called; initial value is +json_class+:
238
- # JSON.create_id # => 'json_class'
239
- def self.create_id=(new_value)
240
- Thread.current[:"JSON.create_id"] = new_value.dup.freeze
241
- end
242
-
243
- # Returns the current create identifier.
244
- # See also JSON.create_id=.
245
- def self.create_id
246
- Thread.current[:"JSON.create_id"] || 'json_class'
247
124
  end
248
125
 
249
126
  NaN = Float::NAN
@@ -257,7 +134,59 @@ module JSON
257
134
 
258
135
  # This exception is raised if a parser error occurs.
259
136
  class ParserError < JSONError
260
- attr_reader :line, :column
137
+ # Line number where the parser encountered an error.
138
+ # Is +nil+ when raised by JSON::ResumableParser.
139
+ attr_reader :line
140
+
141
+ # One-based column number where the parser encountered an error, counted in Unicode codepoints.
142
+ # Is +nil+ when raised by JSON::ResumableParser.
143
+ attr_reader :column
144
+
145
+ # Returns a best effort JSONPath string representing where in the document
146
+ # the parser encountered an error:
147
+ #
148
+ # begin
149
+ # JSON.parse('{"articles": [ { "title": invalid } ]}')
150
+ # rescue JSON::ParserError => error
151
+ # error.json_path # => "$.articles[0].title"
152
+ # end
153
+ def json_path
154
+ return @json_path if String === @json_path
155
+
156
+ if Array === @json_path
157
+ path = build_json_path(@json_path)
158
+ @json_path = path unless frozen?
159
+ return path
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ def build_json_path(segments)
166
+ error = false
167
+ path = segments.filter_map do |segment|
168
+ next if error
169
+
170
+ case segment
171
+ when Integer
172
+ "[#{segment}]"
173
+ when String, Symbol
174
+ if !segment.valid_encoding?
175
+ segment = segment.b.gsub(/["\\]/n, { '"' => '\\"', '\\' => '\\\\' }).force_encoding(Encoding::UTF_8)
176
+ %{["#{segment}"]}
177
+ elsif segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/)
178
+ ".#{segment}"
179
+ else
180
+ segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' })
181
+ %{["#{segment}"]}
182
+ end
183
+ else
184
+ error = true
185
+ nil
186
+ end
187
+ end.join
188
+ "$#{path}".freeze
189
+ end
261
190
  end
262
191
 
263
192
  # This exception is raised if the nesting of parsed data structures is too
@@ -298,7 +227,7 @@ module JSON
298
227
  Fragment = Struct.new(:json) do
299
228
  def initialize(json)
300
229
  unless string = String.try_convert(json)
301
- raise TypeError, " no implicit conversion of #{json.class} into String"
230
+ raise TypeError, "no implicit conversion of #{json.class} into String"
302
231
  end
303
232
 
304
233
  super(string)
@@ -356,19 +285,17 @@ module JSON
356
285
  # ---
357
286
  #
358
287
  # Raises an exception if +source+ is not valid JSON:
359
- # # Raises JSON::ParserError (783: unexpected token at ''):
360
- # JSON.parse('')
288
+ # # Raises JSON::ParserError (unexpected character: 'invalid' at line 1 column 1):
289
+ # JSON.parse('invalid')
361
290
  #
362
- def parse(source, opts = nil)
363
- opts = ParserOptions.prepare(opts) unless opts.nil?
364
- Parser.parse(source, opts)
365
- end
291
+ def parse(source, on_load: nil, object_class: nil, array_class: nil, **options)
292
+ if object_class || array_class
293
+ on_load = ParserOptions.on_load(on_load, object_class, array_class)
294
+ end
366
295
 
367
- PARSE_L_OPTIONS = {
368
- max_nesting: false,
369
- allow_nan: true,
370
- }.freeze
371
- private_constant :PARSE_L_OPTIONS
296
+ options[:on_load] = on_load if on_load
297
+ Parser.parse(source, options)
298
+ end
372
299
 
373
300
  # :call-seq:
374
301
  # JSON.parse!(source, opts) -> object
@@ -381,34 +308,30 @@ module JSON
381
308
  # - Option +max_nesting+, if not provided, defaults to +false+,
382
309
  # which disables checking for nesting depth.
383
310
  # - Option +allow_nan+, if not provided, defaults to +true+.
384
- def parse!(source, opts = nil)
385
- if opts.nil?
386
- parse(source, PARSE_L_OPTIONS)
387
- else
388
- parse(source, PARSE_L_OPTIONS.merge(opts))
389
- end
311
+ def parse!(source, **options)
312
+ parse(source, max_nesting: false, allow_nan: true, **options)
390
313
  end
391
314
 
392
315
  # :call-seq:
393
- # JSON.load_file(path, opts={}) -> object
316
+ # JSON.load_file(path, **) -> object
394
317
  #
395
318
  # Calls:
396
- # parse(File.read(path), opts)
319
+ # parse(File.read(path), **)
397
320
  #
398
321
  # See method #parse.
399
- def load_file(filespec, opts = nil)
400
- parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
322
+ def load_file(filespec, **options)
323
+ parse(File.read(filespec, encoding: Encoding::UTF_8), **options)
401
324
  end
402
325
 
403
326
  # :call-seq:
404
- # JSON.load_file!(path, opts = {})
327
+ # JSON.load_file!(path, **)
405
328
  #
406
329
  # Calls:
407
- # JSON.parse!(File.read(path, opts))
330
+ # JSON.parse!(File.read(path), **)
408
331
  #
409
332
  # See method #parse!
410
- def load_file!(filespec, opts = nil)
411
- parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
333
+ def load_file!(filespec, **options)
334
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), **options)
412
335
  end
413
336
 
414
337
  # :call-seq:
@@ -444,39 +367,17 @@ module JSON
444
367
  #
445
368
  # Raises an exception if +obj+ contains circular references:
446
369
  # a = []; b = []; a.push(b); b.push(a)
447
- # # Raises JSON::NestingError (nesting of 100 is too deep):
370
+ # # Raises JSON::NestingError (nesting of 100 is too deep. Did you try to serialize objects with circular references?):
448
371
  # JSON.generate(a)
449
372
  #
450
373
  def generate(obj, opts = nil)
451
374
  if State === opts
452
375
  opts.generate(obj)
453
376
  else
454
- State.generate(obj, opts, nil)
377
+ State.generate(obj, opts.frozen? ? opts : opts.dup, nil)
455
378
  end
456
379
  end
457
380
 
458
- # :call-seq:
459
- # JSON.fast_generate(obj, opts) -> new_string
460
- #
461
- # Arguments +obj+ and +opts+ here are the same as
462
- # arguments +obj+ and +opts+ in JSON.generate.
463
- #
464
- # By default, generates \JSON data without checking
465
- # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
466
- #
467
- # Raises an exception if +obj+ contains circular references:
468
- # a = []; b = []; a.push(b); b.push(a)
469
- # # Raises SystemStackError (stack level too deep):
470
- # JSON.fast_generate(a)
471
- def fast_generate(obj, opts = nil)
472
- if RUBY_VERSION >= "3.0"
473
- warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
474
- else
475
- warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
476
- end
477
- generate(obj, opts)
478
- end
479
-
480
381
  PRETTY_GENERATE_OPTIONS = {
481
382
  indent: ' ',
482
383
  space: ' ',
@@ -530,43 +431,20 @@ module JSON
530
431
  raise TypeError, "can't convert #{opts.class} into Hash"
531
432
  end
532
433
  end
434
+
533
435
  options = options.merge(opts)
534
436
  end
535
437
 
536
438
  State.generate(obj, options, nil)
537
439
  end
538
440
 
539
- # Sets or returns default options for the JSON.unsafe_load method.
540
- # Initially:
541
- # opts = JSON.load_default_options
542
- # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
543
- deprecated_singleton_attr_accessor :unsafe_load_default_options
544
-
545
- @unsafe_load_default_options = {
546
- :max_nesting => false,
547
- :allow_nan => true,
548
- :allow_blank => true,
549
- :create_additions => true,
550
- }
551
-
552
- # Sets or returns default options for the JSON.load method.
553
- # Initially:
554
- # opts = JSON.load_default_options
555
- # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
556
- deprecated_singleton_attr_accessor :load_default_options
557
-
558
- @load_default_options = {
559
- :allow_nan => true,
560
- :allow_blank => true,
561
- :create_additions => nil,
562
- }
563
441
  # :call-seq:
564
442
  # JSON.unsafe_load(source, options = {}) -> object
565
443
  # JSON.unsafe_load(source, proc = nil, options = {}) -> object
566
444
  #
567
445
  # Returns the Ruby objects created by parsing the given +source+.
568
446
  #
569
- # BEWARE: This method is meant to serialise data from trusted user input,
447
+ # BEWARE: This method is meant to deserialise data from trusted user input,
570
448
  # like from your own database server or clients under your control, it could
571
449
  # be dangerous to allow untrusted users to pass JSON sources into it.
572
450
  #
@@ -586,7 +464,6 @@ module JSON
586
464
  # See details below.
587
465
  # - Argument +opts+, if given, contains a \Hash of options for the parsing.
588
466
  # See {Parsing Options}[#module-JSON-label-Parsing+Options].
589
- # The default options can be changed via method JSON.unsafe_load_default_options=.
590
467
  #
591
468
  # ---
592
469
  #
@@ -691,38 +568,8 @@ module JSON
691
568
  # #<Admin:0x00000000064c41f8
692
569
  # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
693
570
  #
694
- def unsafe_load(source, proc = nil, options = nil)
695
- opts = if options.nil?
696
- if proc && proc.is_a?(Hash)
697
- options, proc = proc, nil
698
- options
699
- else
700
- _unsafe_load_default_options
701
- end
702
- else
703
- _unsafe_load_default_options.merge(options)
704
- end
705
-
706
- unless source.is_a?(String)
707
- if source.respond_to? :to_str
708
- source = source.to_str
709
- elsif source.respond_to? :to_io
710
- source = source.to_io.read
711
- elsif source.respond_to?(:read)
712
- source = source.read
713
- end
714
- end
715
-
716
- if opts[:allow_blank] && (source.nil? || source.empty?)
717
- source = 'null'
718
- end
719
-
720
- if proc
721
- opts = opts.dup
722
- opts[:on_load] = proc.to_proc
723
- end
724
-
725
- parse(source, opts)
571
+ def unsafe_load(source, proc = nil, **options)
572
+ load(source, proc, max_nesting: false, **options)
726
573
  end
727
574
 
728
575
  # :call-seq:
@@ -731,16 +578,6 @@ module JSON
731
578
  #
732
579
  # Returns the Ruby objects created by parsing the given +source+.
733
580
  #
734
- # BEWARE: This method is meant to serialise data from trusted user input,
735
- # like from your own database server or clients under your control, it could
736
- # be dangerous to allow untrusted users to pass JSON sources into it.
737
- # If you must use it, use JSON.unsafe_load instead to make it clear.
738
- #
739
- # Since JSON version 2.8.0, `load` emits a deprecation warning when a
740
- # non native type is deserialized, without `create_additions` being explicitly
741
- # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
742
- # by default.
743
- #
744
581
  # - Argument +source+ must be, or be convertible to, a \String:
745
582
  # - If +source+ responds to instance method +to_str+,
746
583
  # <tt>source.to_str</tt> becomes the source.
@@ -757,7 +594,6 @@ module JSON
757
594
  # See details below.
758
595
  # - Argument +opts+, if given, contains a \Hash of options for the parsing.
759
596
  # See {Parsing Options}[#module-JSON-label-Parsing+Options].
760
- # The default options can be changed via method JSON.load_default_options=.
761
597
  #
762
598
  # ---
763
599
  #
@@ -862,67 +698,37 @@ module JSON
862
698
  # #<Admin:0x00000000064c41f8
863
699
  # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
864
700
  #
865
- def load(source, proc = nil, options = nil)
866
- if proc && options.nil? && proc.is_a?(Hash)
867
- options = proc
868
- proc = nil
869
- end
870
-
871
- opts = if options.nil?
872
- if proc && proc.is_a?(Hash)
873
- options, proc = proc, nil
874
- options
875
- else
876
- _load_default_options
877
- end
878
- else
879
- _load_default_options.merge(options)
880
- end
881
-
701
+ def load(source, proc = nil, allow_blank: true, **options)
882
702
  unless source.is_a?(String)
883
703
  if source.respond_to? :to_str
884
704
  source = source.to_str
705
+ elsif source.respond_to? :read
706
+ source = source.read
885
707
  elsif source.respond_to? :to_io
886
708
  source = source.to_io.read
887
- elsif source.respond_to?(:read)
888
- source = source.read
889
709
  end
890
710
  end
891
711
 
892
- if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
712
+ if allow_blank && (source.nil? || (String === source && source.empty?))
893
713
  source = 'null'
894
714
  end
895
715
 
896
716
  if proc
897
- opts = opts.dup
898
- opts[:on_load] = proc.to_proc
717
+ parse(source, allow_nan: true, on_load: proc.to_proc, **options)
718
+ else
719
+ parse(source, allow_nan: true, **options)
899
720
  end
900
-
901
- parse(source, opts)
902
721
  end
903
722
 
904
- # Sets or returns the default options for the JSON.dump method.
905
- # Initially:
906
- # opts = JSON.dump_default_options
907
- # opts # => {:max_nesting=>false, :allow_nan=>true}
908
- deprecated_singleton_attr_accessor :dump_default_options
909
- @dump_default_options = {
910
- :max_nesting => false,
911
- :allow_nan => true,
912
- }
913
-
914
723
  # :call-seq:
915
- # JSON.dump(obj, io = nil, limit = nil)
724
+ # JSON.dump(obj, io = nil, _deprecated_limit = nil, options = nil)
916
725
  #
917
726
  # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
918
727
  #
919
- # The default options can be changed via method JSON.dump_default_options.
920
- #
921
728
  # - Argument +io+, if given, should respond to method +write+;
922
729
  # the \JSON \String is written to +io+, and +io+ is returned.
923
730
  # If +io+ is not given, the \JSON \String is returned.
924
- # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
925
- #
731
+ # - Argument +_deprecated_limit+ is deprecated, pass the +:max_nesting+ option instead.
926
732
  # ---
927
733
  #
928
734
  # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
@@ -938,99 +744,35 @@ module JSON
938
744
  # puts File.read(path)
939
745
  # Output:
940
746
  # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
941
- def dump(obj, anIO = nil, limit = nil, kwargs = nil)
747
+ def dump(obj, anIO = nil, _deprecated_limit = nil, kwargs = nil)
942
748
  if kwargs.nil?
943
- if limit.nil?
749
+ if _deprecated_limit.nil?
944
750
  if anIO.is_a?(Hash)
945
751
  kwargs = anIO
946
752
  anIO = nil
947
753
  end
948
- elsif limit.is_a?(Hash)
949
- kwargs = limit
950
- limit = nil
754
+ elsif _deprecated_limit.is_a?(Hash)
755
+ kwargs = _deprecated_limit
756
+ _deprecated_limit = nil
951
757
  end
952
758
  end
953
759
 
954
760
  unless anIO.nil?
955
761
  if anIO.respond_to?(:to_io)
956
762
  anIO = anIO.to_io
957
- elsif limit.nil? && !anIO.respond_to?(:write)
958
- anIO, limit = nil, anIO
763
+ elsif _deprecated_limit.nil? && !anIO.respond_to?(:write)
764
+ anIO, _deprecated_limit = nil, anIO
959
765
  end
960
766
  end
961
767
 
962
- opts = JSON._dump_default_options
963
- opts = opts.merge(:max_nesting => limit) if limit
964
- opts = opts.merge(kwargs) if kwargs
965
-
966
- begin
967
- State.generate(obj, opts, anIO)
968
- rescue JSON::NestingError
969
- raise ArgumentError, "exceed depth limit"
970
- end
971
- end
972
-
973
- # :stopdoc:
974
- # All these were meant to be deprecated circa 2009, but were just set as undocumented
975
- # so usage still exist in the wild.
976
- def unparse(...)
977
- if RUBY_VERSION >= "3.0"
978
- warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
979
- else
980
- warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
981
- end
982
- generate(...)
983
- end
984
- module_function :unparse
985
-
986
- def fast_unparse(...)
987
- if RUBY_VERSION >= "3.0"
988
- warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
989
- else
990
- warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
991
- end
992
- generate(...)
993
- end
994
- module_function :fast_unparse
995
-
996
- def pretty_unparse(...)
997
- if RUBY_VERSION >= "3.0"
998
- warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
999
- else
1000
- warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
1001
- end
1002
- pretty_generate(...)
1003
- end
1004
- module_function :fast_unparse
768
+ opts = {
769
+ allow_nan: !(kwargs && kwargs[:rfc8785]),
770
+ }
771
+ opts[:max_nesting] = _deprecated_limit if _deprecated_limit
772
+ opts.merge!(kwargs) if kwargs
1005
773
 
1006
- def restore(...)
1007
- if RUBY_VERSION >= "3.0"
1008
- warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
1009
- else
1010
- warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
1011
- end
1012
- load(...)
774
+ State.generate(obj, opts, anIO)
1013
775
  end
1014
- module_function :restore
1015
-
1016
- class << self
1017
- private
1018
-
1019
- def const_missing(const_name)
1020
- case const_name
1021
- when :PRETTY_STATE_PROTOTYPE
1022
- if RUBY_VERSION >= "3.0"
1023
- warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
1024
- else
1025
- warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
1026
- end
1027
- state.new(PRETTY_GENERATE_OPTIONS)
1028
- else
1029
- super
1030
- end
1031
- end
1032
- end
1033
- # :startdoc:
1034
776
 
1035
777
  # JSON::Coder holds a parser and generator configuration.
1036
778
  #
@@ -1043,10 +785,31 @@ module JSON
1043
785
  # MyApp::JSONC_CODER.load(document)
1044
786
  #
1045
787
  class Coder
788
+ PARSER_OPTIONS = %i(
789
+ max_nesting
790
+ allow_nan
791
+ allow_trailing_comma
792
+ allow_comments
793
+ allow_control_characters
794
+ allow_invalid_escape
795
+ symbolize_names
796
+ freeze
797
+ allow_duplicate_key
798
+ decimal_class
799
+ ).freeze
800
+ private_constant :PARSER_OPTIONS
801
+
802
+ EXCLUDED_GENERATOR_OPTIONS = (PARSER_OPTIONS - %i(
803
+ max_nesting
804
+ allow_nan
805
+ allow_duplicate_key
806
+ )).freeze
807
+ private_constant :EXCLUDED_GENERATOR_OPTIONS
808
+
1046
809
  # :call-seq:
1047
- # JSON.new(options = nil, &block)
810
+ # JSON::Coder.new(**options, &block)
1048
811
  #
1049
- # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
812
+ # Keyword arguments +options+, if given, are options for both parsing and generating.
1050
813
  # See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
1051
814
  # and {Generating Options}[rdoc-ref:JSON@Generating+Options].
1052
815
  #
@@ -1067,17 +830,25 @@ module JSON
1067
830
  #
1068
831
  # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
1069
832
  #
1070
- def initialize(options = nil, &as_json)
1071
- if options.nil?
1072
- options = { strict: true }
1073
- else
1074
- options = options.dup
1075
- options[:strict] = true
833
+ # The conversion callback can also be passed as +as_json:+. If both are given, the block takes precedence.
834
+ # Passing +false+ or +nil+ for +strict:+ raises ArgumentError.
835
+ #
836
+ def initialize(object_class: nil, array_class: nil, on_load: nil, strict: true, **options, &as_json)
837
+ raise ArgumentError, "JSON::Coder requires strict mode" unless strict
838
+
839
+ if object_class || array_class
840
+ on_load = ParserOptions.on_load(on_load, object_class, array_class)
1076
841
  end
1077
- options[:as_json] = as_json if as_json
842
+ parser_options = options.slice(*PARSER_OPTIONS)
843
+ parser_options[:on_load] = on_load if on_load
844
+ @parser_config = Ext::Parser::Config.new(parser_options).freeze
1078
845
 
1079
- @state = State.new(options).freeze
1080
- @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
846
+ generator_options = options.reject { |k, _| EXCLUDED_GENERATOR_OPTIONS.include?(k) }
847
+ @state = State.new(
848
+ **generator_options,
849
+ strict: true,
850
+ as_json: as_json || generator_options[:as_json],
851
+ ).freeze
1081
852
  end
1082
853
 
1083
854
  # call-seq:
@@ -1136,36 +907,6 @@ end
1136
907
  module ::Kernel
1137
908
  private
1138
909
 
1139
- # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
1140
- # one line.
1141
- def j(*objs)
1142
- if RUBY_VERSION >= "3.0"
1143
- warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1144
- else
1145
- warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1
1146
- end
1147
-
1148
- objs.each do |obj|
1149
- puts JSON.generate(obj, :allow_nan => true, :max_nesting => false)
1150
- end
1151
- nil
1152
- end
1153
-
1154
- # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
1155
- # indentation and over many lines.
1156
- def jj(*objs)
1157
- if RUBY_VERSION >= "3.0"
1158
- warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
1159
- else
1160
- warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
1161
- end
1162
-
1163
- objs.each do |obj|
1164
- puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
1165
- end
1166
- nil
1167
- end
1168
-
1169
910
  # If _object_ is string-like, parse the string and return the parsed result as
1170
911
  # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
1171
912
  # structure object and return it.