json 2.7.5 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/json.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
2
4
  /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
3
5
  end rescue nil
@@ -19,7 +21,7 @@ spec = Gem::Specification.new do |s|
19
21
  'wiki_uri' => 'https://github.com/ruby/json/wiki'
20
22
  }
21
23
 
22
- s.required_ruby_version = Gem::Requirement.new(">= 2.3")
24
+ s.required_ruby_version = Gem::Requirement.new(">= 2.7")
23
25
 
24
26
  if java_ext
25
27
  s.description = "A JSON implementation as a JRuby extension."
@@ -48,6 +50,7 @@ spec = Gem::Specification.new do |s|
48
50
 
49
51
  if java_ext
50
52
  s.platform = 'java'
53
+ s.files += Dir["lib/json/ext/**/*.jar"]
51
54
  else
52
55
  s.extensions = Dir["ext/json/**/extconf.rb"]
53
56
  s.files += Dir["ext/json/**/*.{c,h,rl}"]
@@ -35,7 +35,7 @@ class BigDecimal
35
35
  def as_json(*)
36
36
  {
37
37
  JSON.create_id => self.class.name,
38
- 'b' => _dump,
38
+ 'b' => _dump.force_encoding(Encoding::UTF_8),
39
39
  }
40
40
  end
41
41
 
data/lib/json/common.rb CHANGED
@@ -1,4 +1,5 @@
1
- #frozen_string_literal: true
1
+ # frozen_string_literal: true
2
+
2
3
  require 'json/version'
3
4
 
4
5
  module JSON
@@ -25,16 +26,14 @@ module JSON
25
26
  elsif object.respond_to?(:to_str)
26
27
  str = object.to_str
27
28
  if str.is_a?(String)
28
- return JSON.parse(object.to_str, opts)
29
+ return JSON.parse(str, opts)
29
30
  end
30
31
  end
31
32
 
32
33
  JSON.generate(object, opts)
33
34
  end
34
35
 
35
- # Returns the JSON parser class that is used by JSON. This is either
36
- # JSON::Ext::Parser or JSON::Pure::Parser:
37
- # JSON.parser # => JSON::Ext::Parser
36
+ # Returns the JSON parser class that is used by JSON.
38
37
  attr_reader :parser
39
38
 
40
39
  # Set the JSON parser class _parser_ to be used by JSON.
@@ -49,18 +48,9 @@ module JSON
49
48
  # level (absolute namespace path?). If there doesn't exist a constant at
50
49
  # the given path, an ArgumentError is raised.
51
50
  def deep_const_get(path) # :nodoc:
52
- path.to_s.split(/::/).inject(Object) do |p, c|
53
- case
54
- when c.empty? then p
55
- when p.const_defined?(c, true) then p.const_get(c)
56
- else
57
- begin
58
- p.const_missing(c)
59
- rescue NameError => e
60
- raise ArgumentError, "can't get const #{path}: #{e}"
61
- end
62
- end
63
- end
51
+ Object.const_get(path)
52
+ rescue NameError => e
53
+ raise ArgumentError, "can't get const #{path}: #{e}"
64
54
  end
65
55
 
66
56
  # Set the module _generator_ to be used by JSON.
@@ -69,7 +59,7 @@ module JSON
69
59
  @generator = generator
70
60
  generator_methods = generator::GeneratorMethods
71
61
  for const in generator_methods.constants
72
- klass = deep_const_get(const)
62
+ klass = const_get(const)
73
63
  modul = generator_methods.const_get(const)
74
64
  klass.class_eval do
75
65
  instance_methods(false).each do |m|
@@ -106,14 +96,10 @@ module JSON
106
96
  )
107
97
  end
108
98
 
109
- # Returns the JSON generator module that is used by JSON. This is
110
- # either JSON::Ext::Generator or JSON::Pure::Generator:
111
- # JSON.generator # => JSON::Ext::Generator
99
+ # Returns the JSON generator module that is used by JSON.
112
100
  attr_reader :generator
113
101
 
114
- # Sets or Returns the JSON generator state class that is used by JSON. This is
115
- # either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
116
- # JSON.state # => JSON::Ext::Generator::State
102
+ # Sets or Returns the JSON generator state class that is used by JSON.
117
103
  attr_accessor :state
118
104
  end
119
105
 
@@ -157,7 +143,23 @@ module JSON
157
143
  # :startdoc:
158
144
 
159
145
  # This exception is raised if a generator or unparser error occurs.
160
- class GeneratorError < JSONError; end
146
+ class GeneratorError < JSONError
147
+ attr_reader :invalid_object
148
+
149
+ def initialize(message, invalid_object = nil)
150
+ super(message)
151
+ @invalid_object = invalid_object
152
+ end
153
+
154
+ def detailed_message(...)
155
+ if @invalid_object.nil?
156
+ super
157
+ else
158
+ "#{super}\nInvalid object: #{@invalid_object.inspect}"
159
+ end
160
+ end
161
+ end
162
+
161
163
  # For backwards compatibility
162
164
  UnparserError = GeneratorError # :nodoc:
163
165
 
@@ -195,17 +197,17 @@ module JSON
195
197
  # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
196
198
  #
197
199
  # Parses nested JSON objects:
198
- # source = <<-EOT
199
- # {
200
- # "name": "Dave",
201
- # "age" :40,
202
- # "hats": [
203
- # "Cattleman's",
204
- # "Panama",
205
- # "Tophat"
206
- # ]
207
- # }
208
- # EOT
200
+ # source = <<~JSON
201
+ # {
202
+ # "name": "Dave",
203
+ # "age" :40,
204
+ # "hats": [
205
+ # "Cattleman's",
206
+ # "Panama",
207
+ # "Tophat"
208
+ # ]
209
+ # }
210
+ # JSON
209
211
  # ruby = JSON.parse(source)
210
212
  # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
211
213
  #
@@ -216,16 +218,7 @@ module JSON
216
218
  # JSON.parse('')
217
219
  #
218
220
  def parse(source, opts = nil)
219
- if opts.nil?
220
- Parser.new(source).parse
221
- else
222
- # NB: The ** shouldn't be required, but we have to deal with
223
- # different versions of the `json` and `json_pure` gems being
224
- # loaded concurrently.
225
- # Prior to 2.7.3, `JSON::Ext::Parser` would only take kwargs.
226
- # Ref: https://github.com/ruby/json/issues/650
227
- Parser.new(source, **opts).parse
228
- end
221
+ Parser.parse(source, opts)
229
222
  end
230
223
 
231
224
  # :call-seq:
@@ -254,8 +247,8 @@ module JSON
254
247
  # parse(File.read(path), opts)
255
248
  #
256
249
  # See method #parse.
257
- def load_file(filespec, opts = {})
258
- parse(File.read(filespec), opts)
250
+ def load_file(filespec, opts = nil)
251
+ parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
259
252
  end
260
253
 
261
254
  # :call-seq:
@@ -266,7 +259,7 @@ module JSON
266
259
  #
267
260
  # See method #parse!
268
261
  def load_file!(filespec, opts = {})
269
- parse!(File.read(filespec), opts)
262
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
270
263
  end
271
264
 
272
265
  # :call-seq:
@@ -307,11 +300,10 @@ module JSON
307
300
  #
308
301
  def generate(obj, opts = nil)
309
302
  if State === opts
310
- state = opts
303
+ opts.generate(obj)
311
304
  else
312
- state = State.new(opts)
305
+ State.generate(obj, opts, nil)
313
306
  end
314
- state.generate(obj)
315
307
  end
316
308
 
317
309
  # :stopdoc:
@@ -404,6 +396,20 @@ module JSON
404
396
  module_function :pretty_unparse
405
397
  # :startdoc:
406
398
 
399
+ class << self
400
+ # Sets or returns default options for the JSON.unsafe_load method.
401
+ # Initially:
402
+ # opts = JSON.load_default_options
403
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
404
+ attr_accessor :unsafe_load_default_options
405
+ end
406
+ self.unsafe_load_default_options = {
407
+ :max_nesting => false,
408
+ :allow_nan => true,
409
+ :allow_blank => true,
410
+ :create_additions => true,
411
+ }
412
+
407
413
  class << self
408
414
  # Sets or returns default options for the JSON.load method.
409
415
  # Initially:
@@ -412,17 +418,179 @@ module JSON
412
418
  attr_accessor :load_default_options
413
419
  end
414
420
  self.load_default_options = {
415
- :max_nesting => false,
416
421
  :allow_nan => true,
417
422
  :allow_blank => true,
418
- :create_additions => true,
423
+ :create_additions => nil,
419
424
  }
425
+ # :call-seq:
426
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
427
+ #
428
+ # Returns the Ruby objects created by parsing the given +source+.
429
+ #
430
+ # BEWARE: This method is meant to serialise data from trusted user input,
431
+ # like from your own database server or clients under your control, it could
432
+ # be dangerous to allow untrusted users to pass JSON sources into it.
433
+ #
434
+ # - Argument +source+ must be, or be convertible to, a \String:
435
+ # - If +source+ responds to instance method +to_str+,
436
+ # <tt>source.to_str</tt> becomes the source.
437
+ # - If +source+ responds to instance method +to_io+,
438
+ # <tt>source.to_io.read</tt> becomes the source.
439
+ # - If +source+ responds to instance method +read+,
440
+ # <tt>source.read</tt> becomes the source.
441
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
442
+ # - Option +allow_blank+ specifies a truthy value.
443
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
444
+ # - Otherwise, +source+ remains the source.
445
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
446
+ # It will be called recursively with each result (depth-first order).
447
+ # See details below.
448
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
449
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
450
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
451
+ #
452
+ # ---
453
+ #
454
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
455
+ # <tt>parse(source, opts)</tt>; see #parse.
456
+ #
457
+ # Source for following examples:
458
+ # source = <<~JSON
459
+ # {
460
+ # "name": "Dave",
461
+ # "age" :40,
462
+ # "hats": [
463
+ # "Cattleman's",
464
+ # "Panama",
465
+ # "Tophat"
466
+ # ]
467
+ # }
468
+ # JSON
469
+ #
470
+ # Load a \String:
471
+ # ruby = JSON.unsafe_load(source)
472
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
473
+ #
474
+ # Load an \IO object:
475
+ # require 'stringio'
476
+ # object = JSON.unsafe_load(StringIO.new(source))
477
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
478
+ #
479
+ # Load a \File object:
480
+ # path = 't.json'
481
+ # File.write(path, source)
482
+ # File.open(path) do |file|
483
+ # JSON.unsafe_load(file)
484
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
485
+ #
486
+ # ---
487
+ #
488
+ # When +proc+ is given:
489
+ # - Modifies +source+ as above.
490
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
491
+ # - Recursively calls <tt>proc(result)</tt>.
492
+ # - Returns the final result.
493
+ #
494
+ # Example:
495
+ # require 'json'
496
+ #
497
+ # # Some classes for the example.
498
+ # class Base
499
+ # def initialize(attributes)
500
+ # @attributes = attributes
501
+ # end
502
+ # end
503
+ # class User < Base; end
504
+ # class Account < Base; end
505
+ # class Admin < Base; end
506
+ # # The JSON source.
507
+ # json = <<-EOF
508
+ # {
509
+ # "users": [
510
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
511
+ # {"type": "User", "username": "john", "email": "john@example.com"}
512
+ # ],
513
+ # "accounts": [
514
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
515
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
516
+ # ],
517
+ # "admins": {"type": "Admin", "password": "0wn3d"}
518
+ # }
519
+ # EOF
520
+ # # Deserializer method.
521
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
522
+ # type = obj.is_a?(Hash) && obj["type"]
523
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
524
+ # end
525
+ # # Call to JSON.unsafe_load
526
+ # ruby = JSON.unsafe_load(json, proc {|obj|
527
+ # case obj
528
+ # when Hash
529
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
530
+ # when Array
531
+ # obj.map! {|v| deserialize_obj v }
532
+ # end
533
+ # })
534
+ # pp ruby
535
+ # Output:
536
+ # {"users"=>
537
+ # [#<User:0x00000000064c4c98
538
+ # @attributes=
539
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
540
+ # #<User:0x00000000064c4bd0
541
+ # @attributes=
542
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
543
+ # "accounts"=>
544
+ # [{"account"=>
545
+ # #<Account:0x00000000064c4928
546
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
547
+ # {"account"=>
548
+ # #<Account:0x00000000064c4680
549
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
550
+ # "admins"=>
551
+ # #<Admin:0x00000000064c41f8
552
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
553
+ #
554
+ def unsafe_load(source, proc = nil, options = nil)
555
+ opts = if options.nil?
556
+ unsafe_load_default_options
557
+ else
558
+ unsafe_load_default_options.merge(options)
559
+ end
560
+
561
+ unless source.is_a?(String)
562
+ if source.respond_to? :to_str
563
+ source = source.to_str
564
+ elsif source.respond_to? :to_io
565
+ source = source.to_io.read
566
+ elsif source.respond_to?(:read)
567
+ source = source.read
568
+ end
569
+ end
570
+
571
+ if opts[:allow_blank] && (source.nil? || source.empty?)
572
+ source = 'null'
573
+ end
574
+ result = parse(source, opts)
575
+ recurse_proc(result, &proc) if proc
576
+ result
577
+ end
420
578
 
421
579
  # :call-seq:
422
580
  # JSON.load(source, proc = nil, options = {}) -> object
423
581
  #
424
582
  # Returns the Ruby objects created by parsing the given +source+.
425
583
  #
584
+ # BEWARE: This method is meant to serialise data from trusted user input,
585
+ # like from your own database server or clients under your control, it could
586
+ # be dangerous to allow untrusted users to pass JSON sources into it.
587
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
588
+ #
589
+ # Since JSON version 2.8.0, `load` emits a deprecation warning when a
590
+ # non native type is deserialized, without `create_additions` being explicitly
591
+ # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
592
+ # by default.
593
+ #
426
594
  # - Argument +source+ must be, or be convertible to, a \String:
427
595
  # - If +source+ responds to instance method +to_str+,
428
596
  # <tt>source.to_str</tt> becomes the source.
@@ -437,9 +605,6 @@ module JSON
437
605
  # - Argument +proc+, if given, must be a \Proc that accepts one argument.
438
606
  # It will be called recursively with each result (depth-first order).
439
607
  # See details below.
440
- # BEWARE: This method is meant to serialise data from trusted user input,
441
- # like from your own database server or clients under your control, it could
442
- # be dangerous to allow untrusted users to pass JSON sources into it.
443
608
  # - Argument +opts+, if given, contains a \Hash of options for the parsing.
444
609
  # See {Parsing Options}[#module-JSON-label-Parsing+Options].
445
610
  # The default options can be changed via method JSON.load_default_options=.
@@ -450,17 +615,17 @@ module JSON
450
615
  # <tt>parse(source, opts)</tt>; see #parse.
451
616
  #
452
617
  # Source for following examples:
453
- # source = <<-EOT
454
- # {
455
- # "name": "Dave",
456
- # "age" :40,
457
- # "hats": [
458
- # "Cattleman's",
459
- # "Panama",
460
- # "Tophat"
461
- # ]
462
- # }
463
- # EOT
618
+ # source = <<~JSON
619
+ # {
620
+ # "name": "Dave",
621
+ # "age" :40,
622
+ # "hats": [
623
+ # "Cattleman's",
624
+ # "Panama",
625
+ # "Tophat"
626
+ # ]
627
+ # }
628
+ # JSON
464
629
  #
465
630
  # Load a \String:
466
631
  # ruby = JSON.load(source)
@@ -652,18 +817,15 @@ module JSON
652
817
  opts = opts.merge(:max_nesting => limit) if limit
653
818
  opts = merge_dump_options(opts, **kwargs) if kwargs
654
819
 
655
- result = begin
656
- generate(obj, opts)
820
+ begin
821
+ if State === opts
822
+ opts.generate(obj, anIO)
823
+ else
824
+ State.generate(obj, opts, anIO)
825
+ end
657
826
  rescue JSON::NestingError
658
827
  raise ArgumentError, "exceed depth limit"
659
828
  end
660
-
661
- if anIO.nil?
662
- result
663
- else
664
- anIO.write result
665
- anIO
666
- end
667
829
  end
668
830
 
669
831
  # Encodes string using String.encode.
@@ -42,41 +42,22 @@ module JSON
42
42
  raise TypeError, "can't convert #{opts.class} into Hash"
43
43
  end
44
44
  end
45
-
46
- opts.each do |key, value|
47
- case key
48
- when :indent
49
- self.indent = value || ''
50
- when :space
51
- self.space = value || ''
52
- when :space_before
53
- self.space_before = value || ''
54
- when :array_nl
55
- self.array_nl = value || ''
56
- when :object_nl
57
- self.object_nl = value || ''
58
- when :max_nesting
59
- self.max_nesting = value || 0
60
- when :depth
61
- self.depth = value
62
- when :buffer_initial_length
63
- self.buffer_initial_length = value
64
- when :allow_nan
65
- self.allow_nan = value
66
- when :ascii_only
67
- self.ascii_only = value
68
- when :script_safe, :escape_slash
69
- self.script_safe = value
70
- when :strict
71
- self.strict = value
72
- end
73
- end
74
-
75
- self
45
+ _configure(opts)
76
46
  end
77
47
 
78
48
  alias_method :merge, :configure
79
49
 
50
+ # call-seq:
51
+ # generate(obj) -> String
52
+ # generate(obj, anIO) -> anIO
53
+ #
54
+ # Generates a valid JSON document from object +obj+ and returns the
55
+ # result. If no valid JSON document can be created this method raises a
56
+ # GeneratorError exception.
57
+ def generate(obj, io = nil)
58
+ _generate(obj, io)
59
+ end
60
+
80
61
  # call-seq: to_h
81
62
  #
82
63
  # Returns the configuration instance variables as a hash, that can be
data/lib/json/ext.rb CHANGED
@@ -8,14 +8,12 @@ module JSON
8
8
  module Ext
9
9
  if RUBY_ENGINE == 'truffleruby'
10
10
  require 'json/ext/parser'
11
- require 'json/pure'
12
- $DEBUG and warn "Using Ext extension for JSON parser and Pure library for JSON generator."
11
+ require 'json/truffle_ruby/generator'
13
12
  JSON.parser = Parser
14
- JSON.generator = JSON::Pure::Generator
13
+ JSON.generator = ::JSON::TruffleRuby::Generator
15
14
  else
16
15
  require 'json/ext/parser'
17
16
  require 'json/ext/generator'
18
- $DEBUG and warn "Using Ext extension for JSON."
19
17
  JSON.parser = Parser
20
18
  JSON.generator = Generator
21
19
  end