json 2.7.4 → 2.8.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.
- checksums.yaml +4 -4
- data/CHANGES.md +25 -0
- data/README.md +8 -77
- data/ext/json/ext/fbuffer/fbuffer.h +85 -47
- data/ext/json/ext/generator/generator.c +367 -216
- data/ext/json/ext/parser/extconf.rb +5 -27
- data/ext/json/ext/parser/parser.c +1536 -474
- data/ext/json/ext/parser/parser.rl +717 -243
- data/json.gemspec +4 -1
- data/lib/json/add/bigdecimal.rb +1 -1
- data/lib/json/common.rb +200 -54
- data/lib/json/ext/generator/state.rb +1 -31
- data/lib/json/ext.rb +2 -4
- data/lib/json/{pure → truffle_ruby}/generator.rb +165 -124
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +15 -20
- metadata +4 -8
- data/ext/json/ext/generator/generator.h +0 -129
- data/ext/json/ext/parser/parser.h +0 -60
- data/lib/json/pure/parser.rb +0 -331
- data/lib/json/pure.rb +0 -16
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.
|
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}"]
|
data/lib/json/add/bigdecimal.rb
CHANGED
data/lib/json/common.rb
CHANGED
@@ -32,9 +32,7 @@ module JSON
|
|
32
32
|
JSON.generate(object, opts)
|
33
33
|
end
|
34
34
|
|
35
|
-
# Returns the JSON parser class that is used by JSON.
|
36
|
-
# JSON::Ext::Parser or JSON::Pure::Parser:
|
37
|
-
# JSON.parser # => JSON::Ext::Parser
|
35
|
+
# Returns the JSON parser class that is used by JSON.
|
38
36
|
attr_reader :parser
|
39
37
|
|
40
38
|
# Set the JSON parser class _parser_ to be used by JSON.
|
@@ -49,18 +47,9 @@ module JSON
|
|
49
47
|
# level (absolute namespace path?). If there doesn't exist a constant at
|
50
48
|
# the given path, an ArgumentError is raised.
|
51
49
|
def deep_const_get(path) # :nodoc:
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
50
|
+
Object.const_get(path)
|
51
|
+
rescue NameError => e
|
52
|
+
raise ArgumentError, "can't get const #{path}: #{e}"
|
64
53
|
end
|
65
54
|
|
66
55
|
# Set the module _generator_ to be used by JSON.
|
@@ -69,7 +58,7 @@ module JSON
|
|
69
58
|
@generator = generator
|
70
59
|
generator_methods = generator::GeneratorMethods
|
71
60
|
for const in generator_methods.constants
|
72
|
-
klass =
|
61
|
+
klass = const_get(const)
|
73
62
|
modul = generator_methods.const_get(const)
|
74
63
|
klass.class_eval do
|
75
64
|
instance_methods(false).each do |m|
|
@@ -106,14 +95,10 @@ module JSON
|
|
106
95
|
)
|
107
96
|
end
|
108
97
|
|
109
|
-
# Returns the JSON generator module that is used by JSON.
|
110
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
111
|
-
# JSON.generator # => JSON::Ext::Generator
|
98
|
+
# Returns the JSON generator module that is used by JSON.
|
112
99
|
attr_reader :generator
|
113
100
|
|
114
|
-
# Sets or Returns the JSON generator state class that is used by JSON.
|
115
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
116
|
-
# JSON.state # => JSON::Ext::Generator::State
|
101
|
+
# Sets or Returns the JSON generator state class that is used by JSON.
|
117
102
|
attr_accessor :state
|
118
103
|
end
|
119
104
|
|
@@ -195,17 +180,17 @@ module JSON
|
|
195
180
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
196
181
|
#
|
197
182
|
# Parses nested JSON objects:
|
198
|
-
# source =
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
183
|
+
# source = <<~JSON
|
184
|
+
# {
|
185
|
+
# "name": "Dave",
|
186
|
+
# "age" :40,
|
187
|
+
# "hats": [
|
188
|
+
# "Cattleman's",
|
189
|
+
# "Panama",
|
190
|
+
# "Tophat"
|
191
|
+
# ]
|
192
|
+
# }
|
193
|
+
# JSON
|
209
194
|
# ruby = JSON.parse(source)
|
210
195
|
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
211
196
|
#
|
@@ -216,11 +201,7 @@ module JSON
|
|
216
201
|
# JSON.parse('')
|
217
202
|
#
|
218
203
|
def parse(source, opts = nil)
|
219
|
-
|
220
|
-
Parser.new(source).parse
|
221
|
-
else
|
222
|
-
Parser.new(source, opts).parse
|
223
|
-
end
|
204
|
+
Parser.parse(source, opts)
|
224
205
|
end
|
225
206
|
|
226
207
|
# :call-seq:
|
@@ -302,11 +283,10 @@ module JSON
|
|
302
283
|
#
|
303
284
|
def generate(obj, opts = nil)
|
304
285
|
if State === opts
|
305
|
-
|
286
|
+
opts.generate(obj)
|
306
287
|
else
|
307
|
-
|
288
|
+
State.generate(obj, opts)
|
308
289
|
end
|
309
|
-
state.generate(obj)
|
310
290
|
end
|
311
291
|
|
312
292
|
# :stopdoc:
|
@@ -399,6 +379,20 @@ module JSON
|
|
399
379
|
module_function :pretty_unparse
|
400
380
|
# :startdoc:
|
401
381
|
|
382
|
+
class << self
|
383
|
+
# Sets or returns default options for the JSON.unsafe_load method.
|
384
|
+
# Initially:
|
385
|
+
# opts = JSON.load_default_options
|
386
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
387
|
+
attr_accessor :unsafe_load_default_options
|
388
|
+
end
|
389
|
+
self.unsafe_load_default_options = {
|
390
|
+
:max_nesting => false,
|
391
|
+
:allow_nan => true,
|
392
|
+
:allow_blank => true,
|
393
|
+
:create_additions => true,
|
394
|
+
}
|
395
|
+
|
402
396
|
class << self
|
403
397
|
# Sets or returns default options for the JSON.load method.
|
404
398
|
# Initially:
|
@@ -407,11 +401,162 @@ module JSON
|
|
407
401
|
attr_accessor :load_default_options
|
408
402
|
end
|
409
403
|
self.load_default_options = {
|
410
|
-
:max_nesting => false,
|
411
404
|
:allow_nan => true,
|
412
405
|
:allow_blank => true,
|
413
|
-
:create_additions =>
|
406
|
+
:create_additions => nil,
|
414
407
|
}
|
408
|
+
# :call-seq:
|
409
|
+
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
410
|
+
#
|
411
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
412
|
+
#
|
413
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
414
|
+
# - If +source+ responds to instance method +to_str+,
|
415
|
+
# <tt>source.to_str</tt> becomes the source.
|
416
|
+
# - If +source+ responds to instance method +to_io+,
|
417
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
418
|
+
# - If +source+ responds to instance method +read+,
|
419
|
+
# <tt>source.read</tt> becomes the source.
|
420
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
421
|
+
# - Option +allow_blank+ specifies a truthy value.
|
422
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
423
|
+
# - Otherwise, +source+ remains the source.
|
424
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
425
|
+
# It will be called recursively with each result (depth-first order).
|
426
|
+
# See details below.
|
427
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
428
|
+
# like from your own database server or clients under your control, it could
|
429
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
430
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
431
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
432
|
+
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
433
|
+
#
|
434
|
+
# ---
|
435
|
+
#
|
436
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
437
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
438
|
+
#
|
439
|
+
# Source for following examples:
|
440
|
+
# source = <<~JSON
|
441
|
+
# {
|
442
|
+
# "name": "Dave",
|
443
|
+
# "age" :40,
|
444
|
+
# "hats": [
|
445
|
+
# "Cattleman's",
|
446
|
+
# "Panama",
|
447
|
+
# "Tophat"
|
448
|
+
# ]
|
449
|
+
# }
|
450
|
+
# JSON
|
451
|
+
#
|
452
|
+
# Load a \String:
|
453
|
+
# ruby = JSON.unsafe_load(source)
|
454
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
455
|
+
#
|
456
|
+
# Load an \IO object:
|
457
|
+
# require 'stringio'
|
458
|
+
# object = JSON.unsafe_load(StringIO.new(source))
|
459
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
460
|
+
#
|
461
|
+
# Load a \File object:
|
462
|
+
# path = 't.json'
|
463
|
+
# File.write(path, source)
|
464
|
+
# File.open(path) do |file|
|
465
|
+
# JSON.unsafe_load(file)
|
466
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
467
|
+
#
|
468
|
+
# ---
|
469
|
+
#
|
470
|
+
# When +proc+ is given:
|
471
|
+
# - Modifies +source+ as above.
|
472
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
473
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
474
|
+
# - Returns the final result.
|
475
|
+
#
|
476
|
+
# Example:
|
477
|
+
# require 'json'
|
478
|
+
#
|
479
|
+
# # Some classes for the example.
|
480
|
+
# class Base
|
481
|
+
# def initialize(attributes)
|
482
|
+
# @attributes = attributes
|
483
|
+
# end
|
484
|
+
# end
|
485
|
+
# class User < Base; end
|
486
|
+
# class Account < Base; end
|
487
|
+
# class Admin < Base; end
|
488
|
+
# # The JSON source.
|
489
|
+
# json = <<-EOF
|
490
|
+
# {
|
491
|
+
# "users": [
|
492
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
493
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
494
|
+
# ],
|
495
|
+
# "accounts": [
|
496
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
497
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
498
|
+
# ],
|
499
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
500
|
+
# }
|
501
|
+
# EOF
|
502
|
+
# # Deserializer method.
|
503
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
504
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
505
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
506
|
+
# end
|
507
|
+
# # Call to JSON.unsafe_load
|
508
|
+
# ruby = JSON.unsafe_load(json, proc {|obj|
|
509
|
+
# case obj
|
510
|
+
# when Hash
|
511
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
512
|
+
# when Array
|
513
|
+
# obj.map! {|v| deserialize_obj v }
|
514
|
+
# end
|
515
|
+
# })
|
516
|
+
# pp ruby
|
517
|
+
# Output:
|
518
|
+
# {"users"=>
|
519
|
+
# [#<User:0x00000000064c4c98
|
520
|
+
# @attributes=
|
521
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
522
|
+
# #<User:0x00000000064c4bd0
|
523
|
+
# @attributes=
|
524
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
525
|
+
# "accounts"=>
|
526
|
+
# [{"account"=>
|
527
|
+
# #<Account:0x00000000064c4928
|
528
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
529
|
+
# {"account"=>
|
530
|
+
# #<Account:0x00000000064c4680
|
531
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
532
|
+
# "admins"=>
|
533
|
+
# #<Admin:0x00000000064c41f8
|
534
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
535
|
+
#
|
536
|
+
def unsafe_load(source, proc = nil, options = nil)
|
537
|
+
opts = if options.nil?
|
538
|
+
unsafe_load_default_options
|
539
|
+
else
|
540
|
+
unsafe_load_default_options.merge(options)
|
541
|
+
end
|
542
|
+
|
543
|
+
unless source.is_a?(String)
|
544
|
+
if source.respond_to? :to_str
|
545
|
+
source = source.to_str
|
546
|
+
elsif source.respond_to? :to_io
|
547
|
+
source = source.to_io.read
|
548
|
+
elsif source.respond_to?(:read)
|
549
|
+
source = source.read
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
554
|
+
source = 'null'
|
555
|
+
end
|
556
|
+
result = parse(source, opts)
|
557
|
+
recurse_proc(result, &proc) if proc
|
558
|
+
result
|
559
|
+
end
|
415
560
|
|
416
561
|
# :call-seq:
|
417
562
|
# JSON.load(source, proc = nil, options = {}) -> object
|
@@ -435,6 +580,7 @@ module JSON
|
|
435
580
|
# BEWARE: This method is meant to serialise data from trusted user input,
|
436
581
|
# like from your own database server or clients under your control, it could
|
437
582
|
# be dangerous to allow untrusted users to pass JSON sources into it.
|
583
|
+
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
438
584
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
439
585
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
440
586
|
# The default options can be changed via method JSON.load_default_options=.
|
@@ -445,17 +591,17 @@ module JSON
|
|
445
591
|
# <tt>parse(source, opts)</tt>; see #parse.
|
446
592
|
#
|
447
593
|
# Source for following examples:
|
448
|
-
# source =
|
449
|
-
#
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
454
|
-
#
|
455
|
-
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
#
|
594
|
+
# source = <<~JSON
|
595
|
+
# {
|
596
|
+
# "name": "Dave",
|
597
|
+
# "age" :40,
|
598
|
+
# "hats": [
|
599
|
+
# "Cattleman's",
|
600
|
+
# "Panama",
|
601
|
+
# "Tophat"
|
602
|
+
# ]
|
603
|
+
# }
|
604
|
+
# JSON
|
459
605
|
#
|
460
606
|
# Load a \String:
|
461
607
|
# ruby = JSON.load(source)
|
@@ -42,37 +42,7 @@ 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
|
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/
|
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::
|
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
|