json 3.0.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.
@@ -112,6 +112,8 @@ module JSON
112
112
  # while generating a JSON text from a Ruby data structure.
113
113
  class State
114
114
  singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
115
+ singleton_class.attr_accessor :rfc8785_number_formatter_proc # :nodoc:
116
+ singleton_class.attr_accessor :rfc8785_sort_keys_proc # :nodoc:
115
117
 
116
118
  def self.generate(obj, opts = nil, io = nil)
117
119
  new(opts).generate(obj, io)
@@ -125,7 +127,7 @@ module JSON
125
127
  if opts
126
128
  case
127
129
  when self === opts
128
- return opts
130
+ return opts.validate_rfc8785
129
131
  when opts.respond_to?(:to_hash)
130
132
  return new(opts.to_hash)
131
133
  when opts.respond_to?(:to_h)
@@ -168,6 +170,7 @@ module JSON
168
170
  @max_nesting = 100
169
171
  @sort_keys = false
170
172
  @allow_duplicate_key = false
173
+ @rfc8785 = false
171
174
  _configure(**opts) if opts
172
175
  end
173
176
 
@@ -189,7 +192,16 @@ module JSON
189
192
  attr_accessor :array_nl
190
193
 
191
194
  # This proc converts unsupported types into native JSON types.
192
- attr_accessor :as_json
195
+ attr_reader :as_json
196
+
197
+ def as_json=(value)
198
+ raise FrozenError, "can't modify frozen #{self.class}: #{inspect}" if frozen?
199
+ value = value.to_proc if !(Proc === value) && value.respond_to?(:to_proc)
200
+ unless Proc === value
201
+ raise TypeError, "as_json must be a Proc"
202
+ end
203
+ @as_json = value
204
+ end
193
205
 
194
206
  # This integer returns the maximum level of data structure nesting in
195
207
  # the generated JSON, max_nesting = 0 if no maximum is checked.
@@ -209,6 +221,9 @@ module JSON
209
221
  # desired order.
210
222
  attr_reader :sort_keys
211
223
 
224
+ # Controls whether RFC8785 (canonicalization) mode is enabled. Otherwise returns false.
225
+ attr_writer :rfc8785
226
+
212
227
  def sort_keys=(value) # :nodoc:
213
228
  type_error = false
214
229
  @sort_keys = case value
@@ -288,6 +303,27 @@ module JSON
288
303
  @strict
289
304
  end
290
305
 
306
+ # Returns true, if RFC8785 (canonicalization) mode is enabled. Otherwise returns false.
307
+ def rfc8785?
308
+ @rfc8785
309
+ end
310
+
311
+ def validate_rfc8785 # :nodoc:
312
+ if @rfc8785
313
+ option = if !@indent.empty? then :indent
314
+ elsif !@space.empty? then :space
315
+ elsif !@space_before.empty? then :space_before
316
+ elsif !@object_nl.empty? then :object_nl
317
+ elsif !@array_nl.empty? then :array_nl
318
+ elsif @ascii_only then :ascii_only
319
+ elsif @script_safe then :script_safe
320
+ elsif @allow_nan then :allow_nan
321
+ end
322
+ raise ArgumentError, "#{option} cannot be used with rfc8785" if option
323
+ end
324
+ self
325
+ end
326
+
291
327
  # Configure this State instance with the Hash _opts_, and return
292
328
  # itself.
293
329
  def configure(options)
@@ -302,10 +338,13 @@ module JSON
302
338
  end
303
339
  alias merge configure
304
340
 
341
+ # Defaults are the current values so that #configure only writes what it was given.
305
342
  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
343
+ indent: @indent, space: @space, space_before: @space_before, object_nl: @object_nl,
344
+ array_nl: @array_nl, allow_nan: @allow_nan, as_json: @as_json, ascii_only: @ascii_only,
345
+ sort_keys: @sort_keys, depth: @depth, buffer_initial_length: @buffer_initial_length,
346
+ allow_duplicate_key: @allow_duplicate_key, script_safe: @script_safe, strict: @strict,
347
+ max_nesting: @max_nesting, rfc8785: @rfc8785
309
348
  )
310
349
  if depth.negative?
311
350
  raise ArgumentError, "depth must be >= 0 (got #{depth})"
@@ -321,7 +360,11 @@ module JSON
321
360
  @object_nl = object_nl || ''
322
361
  @array_nl = array_nl || ''
323
362
  @allow_nan = allow_nan || false
324
- @as_json = as_json || false
363
+ if as_json
364
+ self.as_json = as_json
365
+ else
366
+ @as_json = false
367
+ end
325
368
  @ascii_only = ascii_only || false
326
369
  self.sort_keys = sort_keys
327
370
  @depth = depth
@@ -330,7 +373,9 @@ module JSON
330
373
  @script_safe = script_safe
331
374
  @strict = strict
332
375
  @max_nesting = max_nesting || 0
333
- self
376
+ self.rfc8785 = rfc8785
377
+
378
+ validate_rfc8785
334
379
  end
335
380
 
336
381
  def allow_duplicate_key? # :nodoc:
@@ -340,13 +385,24 @@ module JSON
340
385
  # Returns the configuration instance variables as a hash, that can be
341
386
  # passed to the configure method.
342
387
  def to_h
343
- result = {}
344
- instance_variables.each do |iv|
345
- key = iv.to_s[1..-1]
346
- result[key.to_sym] = instance_variable_get(iv)
347
- end
348
-
349
- result
388
+ {
389
+ indent: @indent,
390
+ space: @space,
391
+ space_before: @space_before,
392
+ object_nl: @object_nl,
393
+ array_nl: @array_nl,
394
+ as_json: @as_json,
395
+ allow_nan: @allow_nan,
396
+ ascii_only: @ascii_only,
397
+ max_nesting: @max_nesting,
398
+ script_safe: @script_safe,
399
+ strict: @strict,
400
+ depth: @depth,
401
+ buffer_initial_length: @buffer_initial_length,
402
+ sort_keys: @sort_keys,
403
+ allow_duplicate_key: @allow_duplicate_key,
404
+ rfc8785: @rfc8785,
405
+ }
350
406
  end
351
407
 
352
408
  alias to_hash to_h
@@ -359,16 +415,14 @@ module JSON
359
415
  return dup.generate(obj, anIO) if frozen?
360
416
 
361
417
  depth = @depth
418
+ validate_rfc8785
362
419
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
363
420
  !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys
364
421
  result = generate_json(obj, ''.dup)
365
422
  else
366
- if @sort_keys
367
- obj = @sort_keys.call(obj)
368
- end
369
-
370
423
  result = obj.to_json(self)
371
424
  end
425
+
372
426
  JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
373
427
  "source sequence #{result.inspect} is illegal/malformed utf-8",
374
428
  obj
@@ -390,6 +444,11 @@ module JSON
390
444
  buf << '{'
391
445
  first = true
392
446
  key_type = nil
447
+ if @rfc8785
448
+ obj = State.rfc8785_sort_keys_proc.call(obj, @strict && @as_json)
449
+ elsif @sort_keys
450
+ obj = @sort_keys.call(obj)
451
+ end
393
452
  obj.each_pair do |k,v|
394
453
  if first
395
454
  key_type = k.class
@@ -431,7 +490,7 @@ module JSON
431
490
  buf << obj.to_json(self)
432
491
  end
433
492
  when Integer
434
- buf << obj.to_s
493
+ buf << (@rfc8785 ? State.rfc8785_number_formatter_proc.call(obj) : obj.to_s)
435
494
  when Symbol
436
495
  if @strict
437
496
  fast_serialize_string(obj.name, buf)
@@ -500,10 +559,12 @@ module JSON
500
559
  def to_json(state = nil, *)
501
560
  state = State.from_state(state)
502
561
  depth = state.depth
503
- state.check_max_nesting
504
- json_transform(state)
505
- ensure
506
- state.depth = depth
562
+ begin
563
+ state.check_max_nesting
564
+ json_transform(state)
565
+ ensure
566
+ state.depth = depth
567
+ end
507
568
  end
508
569
 
509
570
  private
@@ -521,13 +582,21 @@ module JSON
521
582
  first = true
522
583
  key_type = nil
523
584
  indent = !state.object_nl.empty?
524
- each { |key, value|
585
+ hash = self
586
+
587
+ if state.rfc8785?
588
+ hash = State.rfc8785_sort_keys_proc.call(hash, state.strict? && state.as_json)
589
+ elsif state.sort_keys
590
+ hash = state.sort_keys.call(hash)
591
+ end
592
+
593
+ hash.each { |key, value|
525
594
  if first
526
595
  key_type = key.class
527
596
  else
528
597
  if key_type && !state.allow_duplicate_key? && key_type != key.class
529
598
  key_type = nil # stop checking
530
- JSON.send(:on_mixed_keys_hash, self)
599
+ JSON.send(:on_mixed_keys_hash, hash)
531
600
  end
532
601
  result << delim
533
602
  end
@@ -592,10 +661,12 @@ module JSON
592
661
  def to_json(state = nil, *)
593
662
  state = State.from_state(state)
594
663
  depth = state.depth
595
- state.check_max_nesting
596
- json_transform(state)
597
- ensure
598
- state.depth = depth
664
+ begin
665
+ state.check_max_nesting
666
+ json_transform(state)
667
+ ensure
668
+ state.depth = depth
669
+ end
599
670
  end
600
671
 
601
672
  private
@@ -648,13 +719,20 @@ module JSON
648
719
 
649
720
  module Integer
650
721
  # Returns a JSON string representation for this Integer number.
651
- def to_json(*) to_s end
722
+ def to_json(state = nil, *)
723
+ if state && State.from_state(state).rfc8785?
724
+ State.rfc8785_number_formatter_proc.call(self)
725
+ else
726
+ to_s
727
+ end
728
+ end
652
729
  end
653
730
 
654
731
  module Float
655
732
  # Returns a JSON string representation for this Float number.
656
733
  def to_json(state = nil, *args)
657
734
  state = State.from_state(state)
735
+
658
736
  if infinite? || nan?
659
737
  if state.allow_nan?
660
738
  to_s
@@ -676,6 +754,8 @@ module JSON
676
754
  else
677
755
  raise GeneratorError.new("#{self} not allowed in JSON", self)
678
756
  end
757
+ elsif state.rfc8785?
758
+ State.rfc8785_number_formatter_proc.call(self)
679
759
  else
680
760
  to_s
681
761
  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 = '3.0.2'
4
+ VERSION = '3.1.0'
5
5
  end
data/lib/json.rb CHANGED
@@ -414,6 +414,14 @@ require 'json/common'
414
414
  # When +true+, keys are sorted lexicographically. When a \Proc, it receives
415
415
  # the entire \Hash and must return a \Hash with its pairs in the desired
416
416
  # order, allowing for arbitrary sort orders.
417
+ # - Option +rfc8785+ (boolean) controls whether the generated JSON will be canonicalized
418
+ # as defined in RFC8785.
419
+ # Integers are converted to Float before formatting and may be rounded.
420
+ # Integers outside the finite Float range raise JSON::GeneratorError.
421
+ # Nonempty +indent+, +space+, +space_before+, +object_nl+, or +array_nl+,
422
+ # and enabled +ascii_only+, +script_safe+, or +allow_nan+ raise ArgumentError.
423
+ # JSON.dump defaults +allow_nan+ to +false+ when +rfc8785+ is enabled.
424
+ # JSON::Fragment contents are inserted as is; the caller must ensure they are canonical.
417
425
  #
418
426
  # In this example, +obj+ is used first to generate the shortest
419
427
  # \JSON data (no whitespace), then again with all formatting options
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -68,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  requirements: []
71
- rubygems_version: 4.0.16
71
+ rubygems_version: 4.0.20
72
72
  specification_version: 4
73
73
  summary: JSON Implementation for Ruby
74
74
  test_files: []