json 2.13.2 → 3.0.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.
@@ -8,20 +8,8 @@ module JSON
8
8
  #
9
9
  # Instantiates a new State object, configured by _opts_.
10
10
  #
11
- # _opts_ can have the following keys:
12
- #
13
- # * *indent*: a string used to indent levels (default: ''),
14
- # * *space*: a string that is put after, a : or , delimiter (default: ''),
15
- # * *space_before*: a string that is put before a : pair delimiter (default: ''),
16
- # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
17
- # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
18
- # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
19
- # generated, otherwise an exception is thrown, if these values are
20
- # encountered. This options defaults to false.
21
- # * *ascii_only*: true if only ASCII characters should be generated. This
22
- # option defaults to false.
23
- # * *buffer_initial_length*: sets the initial length of the generator's
24
- # internal buffer.
11
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
12
+ # See {Generating Options}[rdoc-ref:JSON@Generating+Options].
25
13
  def initialize(opts = nil)
26
14
  if opts && !opts.empty?
27
15
  configure(opts)
@@ -66,8 +54,14 @@ module JSON
66
54
  strict: strict?,
67
55
  depth: depth,
68
56
  buffer_initial_length: buffer_initial_length,
57
+ sort_keys: sort_keys
69
58
  }
70
59
 
60
+ allow_duplicate_key = allow_duplicate_key?
61
+ unless allow_duplicate_key.nil?
62
+ result[:allow_duplicate_key] = allow_duplicate_key
63
+ end
64
+
71
65
  instance_variables.each do |iv|
72
66
  iv = iv.to_s[1..-1]
73
67
  result[iv.to_sym] = self[iv]
@@ -77,29 +71,6 @@ module JSON
77
71
  end
78
72
 
79
73
  alias_method :to_hash, :to_h
80
-
81
- # call-seq: [](name)
82
- #
83
- # Returns the value returned by method +name+.
84
- def [](name)
85
- if respond_to?(name)
86
- __send__(name)
87
- else
88
- instance_variable_get("@#{name}") if
89
- instance_variables.include?("@#{name}".to_sym) # avoid warning
90
- end
91
- end
92
-
93
- # call-seq: []=(name, value)
94
- #
95
- # Sets the attribute name to value.
96
- def []=(name, value)
97
- if respond_to?(name_writer = "#{name}=")
98
- __send__ name_writer, value
99
- else
100
- instance_variable_set "@#{name}", value
101
- end
102
- end
103
74
  end
104
75
  end
105
76
  end
data/lib/json/ext.rb CHANGED
@@ -41,5 +41,29 @@ module JSON
41
41
  end
42
42
  end
43
43
 
44
- JSON_LOADED = true unless defined?(JSON::JSON_LOADED)
44
+ if defined?(ResumableParser) # Not yet available on JRuby
45
+ class ResumableParser
46
+ # Returns whether the parser is entirely done: no unconsumed bytes in
47
+ # the buffer, no document under construction and no parsed value
48
+ # awaiting retrieval.
49
+ #
50
+ # The main use case is detecting a truncated stream once the input is
51
+ # exhausted:
52
+ #
53
+ # loop do
54
+ # begin
55
+ # parser << socket.readpartial(4096)
56
+ # rescue EOFError
57
+ # break
58
+ # end
59
+ # while parser.parse
60
+ # process(parser.value)
61
+ # end
62
+ # end
63
+ # warn "stream was truncated" unless parser.empty?
64
+ def empty?
65
+ eos? && !partial_value? && !value?
66
+ end
67
+ end
68
+ end
45
69
  end
@@ -47,6 +47,19 @@ module JSON
47
47
 
48
48
  SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
49
49
 
50
+ def self.native_type?(value) # :nodoc:
51
+ (false == value || true == value || nil == value || String === value || Symbol === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
52
+ end
53
+
54
+ def self.native_key?(key) # :nodoc:
55
+ (Symbol === key || String === key)
56
+ end
57
+
58
+ def self.valid_encoding?(string) # :nodoc:
59
+ return false unless string.encoding == ::Encoding::UTF_8 || string.encoding == ::Encoding::US_ASCII
60
+ string.is_a?(Symbol) || string.valid_encoding?
61
+ end
62
+
50
63
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
51
64
  # UTF16 big endian characters as \u????, and return it.
52
65
  def self.utf8_to_json(string, script_safe = false) # :nodoc:
@@ -98,6 +111,8 @@ module JSON
98
111
  # This class is used to create State instances, that are use to hold data
99
112
  # while generating a JSON text from a Ruby data structure.
100
113
  class State
114
+ singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
115
+
101
116
  def self.generate(obj, opts = nil, io = nil)
102
117
  new(opts).generate(obj, io)
103
118
  end
@@ -151,7 +166,9 @@ module JSON
151
166
  @script_safe = false
152
167
  @strict = false
153
168
  @max_nesting = 100
154
- configure(opts) if opts
169
+ @sort_keys = false
170
+ @allow_duplicate_key = false
171
+ _configure(**opts) if opts
155
172
  end
156
173
 
157
174
  # This string is used to indent levels in the JSON text.
@@ -186,6 +203,33 @@ module JSON
186
203
  # supported by the JSON spec will raise a JSON::GeneratorError
187
204
  attr_accessor :strict
188
205
 
206
+ # Controls key sorting in the generated JSON. If set to +true+, object
207
+ # keys are sorted by key lexicographically. If set to a Proc, it
208
+ # receives the entire Hash and must return a Hash with its pairs in the
209
+ # desired order.
210
+ attr_reader :sort_keys
211
+
212
+ def sort_keys=(value) # :nodoc:
213
+ type_error = false
214
+ @sort_keys = case value
215
+ when Proc
216
+ value
217
+ when true
218
+ State.default_sort_keys_proc
219
+ when nil, false
220
+ false
221
+ else
222
+ type_error = true
223
+ false
224
+ end
225
+
226
+ if type_error
227
+ raise TypeError, "The `sort_keys` argument must be a boolean or a Proc"
228
+ end
229
+
230
+ @sort_keys
231
+ end
232
+
189
233
  # :stopdoc:
190
234
  attr_reader :buffer_initial_length
191
235
 
@@ -198,13 +242,20 @@ module JSON
198
242
 
199
243
  # This integer returns the current depth data structure nesting in the
200
244
  # generated JSON.
201
- attr_accessor :depth
245
+ attr_reader :depth
246
+
247
+ def depth=(depth)
248
+ if depth.negative?
249
+ raise ArgumentError, "depth must be >= 0 (got #{depth})"
250
+ end
251
+ @depth = depth
252
+ end
202
253
 
203
254
  def check_max_nesting # :nodoc:
204
255
  return if @max_nesting.zero?
205
256
  current_nesting = depth + 1
206
257
  current_nesting > @max_nesting and
207
- raise NestingError, "nesting of #{current_nesting} is too deep"
258
+ raise NestingError, "nesting of #{current_nesting} is too deep. Did you try to serialize objects with circular references?"
208
259
  end
209
260
 
210
261
  # Returns true, if circular data structures are checked,
@@ -239,59 +290,62 @@ module JSON
239
290
 
240
291
  # Configure this State instance with the Hash _opts_, and return
241
292
  # itself.
242
- def configure(opts)
243
- if opts.respond_to?(:to_hash)
244
- opts = opts.to_hash
245
- elsif opts.respond_to?(:to_h)
246
- opts = opts.to_h
247
- else
248
- raise TypeError, "can't convert #{opts.class} into Hash"
249
- end
250
- opts.each do |key, value|
251
- instance_variable_set "@#{key}", value
293
+ def configure(options)
294
+ unless Hash == options
295
+ if options.respond_to?(:to_hash)
296
+ options = options.to_hash
297
+ elsif options.respond_to?(:to_h)
298
+ options = options.to_h
299
+ end
252
300
  end
301
+ _configure(**options)
302
+ end
303
+ alias merge configure
253
304
 
254
- # NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json
255
- @indent = opts[:indent] || '' if opts.key?(:indent)
256
- @space = opts[:space] || '' if opts.key?(:space)
257
- @space_before = opts[:space_before] || '' if opts.key?(:space_before)
258
- @object_nl = opts[:object_nl] || '' if opts.key?(:object_nl)
259
- @array_nl = opts[:array_nl] || '' if opts.key?(:array_nl)
260
- @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
261
- @as_json = opts[:as_json].to_proc if opts[:as_json]
262
- @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
263
- @depth = opts[:depth] || 0
264
- @buffer_initial_length ||= opts[:buffer_initial_length]
265
-
266
- @script_safe = if opts.key?(:script_safe)
267
- !!opts[:script_safe]
268
- elsif opts.key?(:escape_slash)
269
- !!opts[:escape_slash]
270
- else
271
- false
305
+ private def _configure(
306
+ indent: '', space: '', space_before: '', object_nl: '', array_nl: '', allow_nan: false,
307
+ as_json: false, ascii_only: false, sort_keys: false, depth: 0, buffer_initial_length: 1024,
308
+ allow_duplicate_key: false, script_safe: false, strict: false, max_nesting: 100
309
+ )
310
+ if depth.negative?
311
+ raise ArgumentError, "depth must be >= 0 (got #{depth})"
272
312
  end
273
313
 
274
- @strict = !!opts[:strict] if opts.key?(:strict)
275
-
276
- if !opts.key?(:max_nesting) # defaults to 100
277
- @max_nesting = 100
278
- elsif opts[:max_nesting]
279
- @max_nesting = opts[:max_nesting]
280
- else
281
- @max_nesting = 0
314
+ if max_nesting && !(Integer === max_nesting)
315
+ raise TypeError, ":max_nesting must be `false` or an Integer, got: #{max_nesting.class}"
282
316
  end
317
+
318
+ @indent = indent || ''
319
+ @space = space || ''
320
+ @space_before = space_before || ''
321
+ @object_nl = object_nl || ''
322
+ @array_nl = array_nl || ''
323
+ @allow_nan = allow_nan || false
324
+ @as_json = as_json || false
325
+ @ascii_only = ascii_only || false
326
+ self.sort_keys = sort_keys
327
+ @depth = depth
328
+ @buffer_initial_length = buffer_initial_length
329
+ @allow_duplicate_key = allow_duplicate_key
330
+ @script_safe = script_safe
331
+ @strict = strict
332
+ @max_nesting = max_nesting || 0
283
333
  self
284
334
  end
285
- alias merge configure
335
+
336
+ def allow_duplicate_key? # :nodoc:
337
+ @allow_duplicate_key
338
+ end
286
339
 
287
340
  # Returns the configuration instance variables as a hash, that can be
288
341
  # passed to the configure method.
289
342
  def to_h
290
343
  result = {}
291
344
  instance_variables.each do |iv|
292
- iv = iv.to_s[1..-1]
293
- result[iv.to_sym] = self[iv]
345
+ key = iv.to_s[1..-1]
346
+ result[key.to_sym] = instance_variable_get(iv)
294
347
  end
348
+
295
349
  result
296
350
  end
297
351
 
@@ -302,10 +356,17 @@ module JSON
302
356
  # created this method raises a
303
357
  # GeneratorError exception.
304
358
  def generate(obj, anIO = nil)
359
+ return dup.generate(obj, anIO) if frozen?
360
+
361
+ depth = @depth
305
362
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
306
- !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
363
+ !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys
307
364
  result = generate_json(obj, ''.dup)
308
365
  else
366
+ if @sort_keys
367
+ obj = @sort_keys.call(obj)
368
+ end
369
+
309
370
  result = obj.to_json(self)
310
371
  end
311
372
  JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
@@ -318,10 +379,8 @@ module JSON
318
379
  else
319
380
  result
320
381
  end
321
- end
322
-
323
- def generate_new(obj, anIO = nil) # :nodoc:
324
- dup.generate(obj, anIO)
382
+ ensure
383
+ @depth = depth unless frozen?
325
384
  end
326
385
 
327
386
  # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
@@ -330,8 +389,17 @@ module JSON
330
389
  when Hash
331
390
  buf << '{'
332
391
  first = true
392
+ key_type = nil
333
393
  obj.each_pair do |k,v|
334
- buf << ',' unless first
394
+ if first
395
+ key_type = k.class
396
+ else
397
+ if key_type && !@allow_duplicate_key && key_type != k.class
398
+ key_type = nil # stop checking
399
+ JSON.send(:on_mixed_keys_hash, obj)
400
+ end
401
+ buf << ','
402
+ end
335
403
 
336
404
  key_str = k.to_s
337
405
  if key_str.class == String
@@ -395,24 +463,6 @@ module JSON
395
463
  end
396
464
  buf << '"'
397
465
  end
398
-
399
- # Return the value returned by method +name+.
400
- def [](name)
401
- if respond_to?(name)
402
- __send__(name)
403
- else
404
- instance_variable_get("@#{name}") if
405
- instance_variables.include?("@#{name}".to_sym) # avoid warning
406
- end
407
- end
408
-
409
- def []=(name, value)
410
- if respond_to?(name_writer = "#{name}=")
411
- __send__ name_writer, value
412
- else
413
- instance_variable_set "@#{name}", value
414
- end
415
- end
416
466
  end
417
467
 
418
468
  module GeneratorMethods
@@ -424,10 +474,10 @@ module JSON
424
474
  state = State.from_state(state) if state
425
475
  if state&.strict?
426
476
  value = self
427
- if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
477
+ if state.strict? && !Generator.native_type?(value)
428
478
  if state.as_json
429
- value = state.as_json.call(value)
430
- unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
479
+ value = state.as_json.call(value, false)
480
+ unless Generator.native_type?(value)
431
481
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
432
482
  end
433
483
  value.to_json(state)
@@ -449,33 +499,54 @@ module JSON
449
499
  # _depth_ is used to find out nesting depth, to indent accordingly.
450
500
  def to_json(state = nil, *)
451
501
  state = State.from_state(state)
502
+ depth = state.depth
452
503
  state.check_max_nesting
453
504
  json_transform(state)
505
+ ensure
506
+ state.depth = depth
454
507
  end
455
508
 
456
509
  private
457
510
 
458
- def json_shift(state)
459
- state.object_nl.empty? or return ''
460
- state.indent * state.depth
461
- end
462
-
463
511
  def json_transform(state)
464
512
  depth = state.depth += 1
465
513
 
466
514
  if empty?
467
515
  state.depth -= 1
468
- return '{}'
516
+ return +'{}'
469
517
  end
470
518
 
471
519
  delim = ",#{state.object_nl}"
472
- result = +"{#{state.object_nl}"
520
+ result = "{#{state.object_nl}"
473
521
  first = true
522
+ key_type = nil
474
523
  indent = !state.object_nl.empty?
475
524
  each { |key, value|
476
- result << delim unless first
525
+ if first
526
+ key_type = key.class
527
+ else
528
+ if key_type && !state.allow_duplicate_key? && key_type != key.class
529
+ key_type = nil # stop checking
530
+ JSON.send(:on_mixed_keys_hash, self)
531
+ end
532
+ result << delim
533
+ end
477
534
  result << state.indent * depth if indent
478
535
 
536
+ if state.strict?
537
+ if state.as_json && (!Generator.native_key?(key) || !Generator.valid_encoding?(key))
538
+ key = state.as_json.call(key, true)
539
+ end
540
+
541
+ unless Generator.native_key?(key)
542
+ raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
543
+ end
544
+
545
+ unless Generator.valid_encoding?(key)
546
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
547
+ end
548
+ end
549
+
479
550
  key_str = key.to_s
480
551
  if key_str.is_a?(String)
481
552
  key_json = key_str.to_json(state)
@@ -483,25 +554,27 @@ module JSON
483
554
  raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String"
484
555
  end
485
556
 
486
- result = +"#{result}#{key_json}#{state.space_before}:#{state.space}"
487
- if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
557
+ result = "#{result}#{key_json}#{state.space_before}:#{state.space}"
558
+ if state.strict? && !Generator.native_type?(value)
488
559
  if state.as_json
489
- value = state.as_json.call(value)
490
- unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
560
+ value = state.as_json.call(value, false)
561
+ unless Generator.native_type?(value)
491
562
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
492
563
  end
493
564
  result << value.to_json(state)
565
+ state.depth = depth
494
566
  else
495
567
  raise GeneratorError.new("#{value.class} not allowed in JSON", value)
496
568
  end
497
569
  elsif value.respond_to?(:to_json)
498
570
  result << value.to_json(state)
571
+ state.depth = depth
499
572
  else
500
573
  result << %{"#{String(value)}"}
501
574
  end
502
575
  first = false
503
576
  }
504
- depth = state.depth -= 1
577
+ depth -= 1
505
578
  unless first
506
579
  result << state.object_nl
507
580
  result << state.indent * depth if indent
@@ -518,8 +591,11 @@ module JSON
518
591
  # produced JSON string output further.
519
592
  def to_json(state = nil, *)
520
593
  state = State.from_state(state)
594
+ depth = state.depth
521
595
  state.check_max_nesting
522
596
  json_transform(state)
597
+ ensure
598
+ state.depth = depth
523
599
  end
524
600
 
525
601
  private
@@ -529,7 +605,7 @@ module JSON
529
605
 
530
606
  if empty?
531
607
  state.depth -= 1
532
- return '[]'
608
+ return +'[]'
533
609
  end
534
610
 
535
611
  result = '['.dup
@@ -545,10 +621,10 @@ module JSON
545
621
  each { |value|
546
622
  result << delim unless first
547
623
  result << state.indent * depth if indent
548
- if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol == value)
624
+ if state.strict? && !Generator.native_type?(value)
549
625
  if state.as_json
550
- value = state.as_json.call(value)
551
- unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol === value
626
+ value = state.as_json.call(value, false)
627
+ unless Generator.native_type?(value)
552
628
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
553
629
  end
554
630
  result << value.to_json(state)
@@ -557,12 +633,13 @@ module JSON
557
633
  end
558
634
  elsif value.respond_to?(:to_json)
559
635
  result << value.to_json(state)
636
+ state.depth = depth
560
637
  else
561
638
  result << %{"#{String(value)}"}
562
639
  end
563
640
  first = false
564
641
  }
565
- depth = state.depth -= 1
642
+ depth -= 1
566
643
  result << state.array_nl
567
644
  result << state.indent * depth if indent
568
645
  result << ']'
@@ -582,11 +659,14 @@ module JSON
582
659
  if state.allow_nan?
583
660
  to_s
584
661
  elsif state.strict? && state.as_json
585
- casted_value = state.as_json.call(self)
662
+ casted_value = state.as_json.call(self, false)
586
663
 
587
664
  if casted_value.equal?(self)
588
665
  raise GeneratorError.new("#{self} not allowed in JSON", self)
589
666
  end
667
+ unless Generator.native_type?(casted_value)
668
+ raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
669
+ end
590
670
 
591
671
  state.check_max_nesting
592
672
  state.depth += 1
@@ -619,14 +699,25 @@ module JSON
619
699
  # \u????.
620
700
  def to_json(state = nil, *args)
621
701
  state = State.from_state(state)
622
- if encoding == ::Encoding::UTF_8
623
- unless valid_encoding?
702
+ string = self
703
+
704
+ if state.strict? && state.as_json
705
+ unless Generator.valid_encoding?(string)
706
+ string = state.as_json.call(string, false)
707
+ unless string.is_a?(::String)
708
+ return string.to_json(state, *args)
709
+ end
710
+ end
711
+ end
712
+
713
+ if string.encoding == ::Encoding::UTF_8
714
+ unless string.valid_encoding?
624
715
  raise GeneratorError.new("source sequence is illegal/malformed utf-8", self)
625
716
  end
626
- string = self
627
717
  else
628
- string = encode(::Encoding::UTF_8)
718
+ string = string.encode(::Encoding::UTF_8)
629
719
  end
720
+
630
721
  if state.ascii_only?
631
722
  %("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}")
632
723
  else
@@ -635,54 +726,21 @@ module JSON
635
726
  rescue Encoding::UndefinedConversionError => error
636
727
  raise ::JSON::GeneratorError.new(error.message, self)
637
728
  end
638
-
639
- # Module that holds the extending methods if, the String module is
640
- # included.
641
- module Extend
642
- # Raw Strings are JSON Objects (the raw bytes are stored in an
643
- # array for the key "raw"). The Ruby String can be created by this
644
- # module method.
645
- def json_create(o)
646
- o['raw'].pack('C*')
647
- end
648
- end
649
-
650
- # Extends _modul_ with the String::Extend module.
651
- def self.included(modul)
652
- modul.extend Extend
653
- end
654
-
655
- # This method creates a raw object hash, that can be nested into
656
- # other data structures and will be unparsed as a raw string. This
657
- # method should be used, if you want to convert raw strings to JSON
658
- # instead of UTF-8 strings, e. g. binary data.
659
- def to_json_raw_object
660
- {
661
- JSON.create_id => self.class.name,
662
- 'raw' => self.unpack('C*'),
663
- }
664
- end
665
-
666
- # This method creates a JSON text from the result of
667
- # a call to to_json_raw_object of this String.
668
- def to_json_raw(*args)
669
- to_json_raw_object.to_json(*args)
670
- end
671
729
  end
672
730
 
673
731
  module TrueClass
674
732
  # Returns a JSON string for true: 'true'.
675
- def to_json(*) 'true' end
733
+ def to_json(*) +'true' end
676
734
  end
677
735
 
678
736
  module FalseClass
679
737
  # Returns a JSON string for false: 'false'.
680
- def to_json(*) 'false' end
738
+ def to_json(*) +'false' end
681
739
  end
682
740
 
683
741
  module NilClass
684
742
  # Returns a JSON string for nil: 'null'.
685
- def to_json(*) 'null' end
743
+ def to_json(*) +'null' end
686
744
  end
687
745
  end
688
746
  end
data/lib/json/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.13.2'
4
+ VERSION = '3.0.0'
5
5
  end