json 2.18.1 → 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.
- checksums.yaml +4 -4
- data/CHANGES.md +127 -0
- data/LEGAL +3 -3
- data/README.md +19 -90
- data/ext/json/ext/fbuffer/fbuffer.h +46 -36
- data/ext/json/ext/generator/extconf.rb +3 -0
- data/ext/json/ext/generator/generator.c +200 -364
- data/ext/json/ext/json.h +95 -0
- data/ext/json/ext/parser/extconf.rb +27 -0
- data/ext/json/ext/parser/parser.c +1654 -406
- data/ext/json/ext/simd/simd.h +0 -10
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/common.rb +184 -411
- data/lib/json/ext/generator/state.rb +2 -28
- data/lib/json/ext.rb +25 -1
- data/lib/json/truffle_ruby/generator.rb +89 -81
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +53 -264
- metadata +3 -19
- data/ext/json/ext/vendor/ryu.h +0 -819
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -13
- data/lib/json/add/date.rb +0 -54
- data/lib/json/add/date_time.rb +0 -67
- data/lib/json/add/exception.rb +0 -49
- data/lib/json/add/ostruct.rb +0 -54
- data/lib/json/add/range.rb +0 -54
- data/lib/json/add/rational.rb +0 -49
- data/lib/json/add/regexp.rb +0 -48
- data/lib/json/add/set.rb +0 -48
- data/lib/json/add/string.rb +0 -35
- data/lib/json/add/struct.rb +0 -52
- data/lib/json/add/symbol.rb +0 -52
- data/lib/json/add/time.rb +0 -52
- data/lib/json/generic_object.rb +0 -67
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -155,16 +79,27 @@ module JSON
|
|
|
155
79
|
# Set the module _generator_ to be used by JSON.
|
|
156
80
|
def generator=(generator) # :nodoc:
|
|
157
81
|
old, $VERBOSE = $VERBOSE, nil
|
|
82
|
+
|
|
83
|
+
# The default proc used when the +sort_keys+ generation option is +true+.
|
|
84
|
+
# It returns a new hash with the entries sorted by their keys.
|
|
85
|
+
sort_keys_proc = ->(hash) { hash.sort.to_h }
|
|
86
|
+
if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
|
|
87
|
+
sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
|
|
88
|
+
end
|
|
89
|
+
generator::State.default_sort_keys_proc = sort_keys_proc
|
|
90
|
+
|
|
158
91
|
@generator = generator
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
92
|
+
if generator.const_defined?(:GeneratorMethods)
|
|
93
|
+
generator_methods = generator::GeneratorMethods
|
|
94
|
+
for const in generator_methods.constants
|
|
95
|
+
klass = const_get(const)
|
|
96
|
+
modul = generator_methods.const_get(const)
|
|
97
|
+
klass.class_eval do
|
|
98
|
+
instance_methods(false).each do |m|
|
|
99
|
+
m.to_s == 'to_json' and remove_method m
|
|
100
|
+
end
|
|
101
|
+
include modul
|
|
166
102
|
end
|
|
167
|
-
include modul
|
|
168
103
|
end
|
|
169
104
|
end
|
|
170
105
|
self.state = generator::State
|
|
@@ -182,57 +117,18 @@ module JSON
|
|
|
182
117
|
private
|
|
183
118
|
|
|
184
119
|
# Called from the extension when a hash has both string and symbol keys
|
|
185
|
-
def on_mixed_keys_hash(hash
|
|
120
|
+
def on_mixed_keys_hash(hash)
|
|
186
121
|
set = {}
|
|
187
122
|
hash.each_key do |key|
|
|
188
123
|
key_str = key.to_s
|
|
189
124
|
|
|
190
125
|
if set[key_str]
|
|
191
|
-
|
|
192
|
-
if do_raise
|
|
193
|
-
raise GeneratorError, message
|
|
194
|
-
else
|
|
195
|
-
deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
|
|
196
|
-
end
|
|
126
|
+
raise GeneratorError, "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
|
|
197
127
|
else
|
|
198
128
|
set[key_str] = true
|
|
199
129
|
end
|
|
200
130
|
end
|
|
201
131
|
end
|
|
202
|
-
|
|
203
|
-
def deprecated_singleton_attr_accessor(*attrs)
|
|
204
|
-
args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
|
|
205
|
-
attrs.each do |attr|
|
|
206
|
-
singleton_class.class_eval <<~RUBY
|
|
207
|
-
def #{attr}
|
|
208
|
-
warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
|
|
209
|
-
@#{attr}
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def #{attr}=(val)
|
|
213
|
-
warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
|
|
214
|
-
@#{attr} = val
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def _#{attr}
|
|
218
|
-
@#{attr}
|
|
219
|
-
end
|
|
220
|
-
RUBY
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Sets create identifier, which is used to decide if the _json_create_
|
|
226
|
-
# hook of a class should be called; initial value is +json_class+:
|
|
227
|
-
# JSON.create_id # => 'json_class'
|
|
228
|
-
def self.create_id=(new_value)
|
|
229
|
-
Thread.current[:"JSON.create_id"] = new_value.dup.freeze
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Returns the current create identifier.
|
|
233
|
-
# See also JSON.create_id=.
|
|
234
|
-
def self.create_id
|
|
235
|
-
Thread.current[:"JSON.create_id"] || 'json_class'
|
|
236
132
|
end
|
|
237
133
|
|
|
238
134
|
NaN = Float::NAN
|
|
@@ -246,7 +142,56 @@ module JSON
|
|
|
246
142
|
|
|
247
143
|
# This exception is raised if a parser error occurs.
|
|
248
144
|
class ParserError < JSONError
|
|
249
|
-
|
|
145
|
+
# Line number where the parser encountered an error.
|
|
146
|
+
# Is <tt>nil</tt> when raised by JSON::ResumableParser.
|
|
147
|
+
attr_reader :line
|
|
148
|
+
|
|
149
|
+
# Column number where the parser encountered an error.
|
|
150
|
+
# Is <tt>nil</tt> when raised by JSON::ResumableParser.
|
|
151
|
+
attr_reader :column
|
|
152
|
+
|
|
153
|
+
# Returns a best effort JSONPath string representing where in the document
|
|
154
|
+
# the parser encountered an error:
|
|
155
|
+
#
|
|
156
|
+
# begin
|
|
157
|
+
# JSON.parse('{"articles": [ { "title": invalid } ]}')
|
|
158
|
+
# rescue JSON::ParserError => error
|
|
159
|
+
# error.json_path # => "$.articles[0].title"
|
|
160
|
+
# end
|
|
161
|
+
def json_path
|
|
162
|
+
return @json_path if String === @json_path
|
|
163
|
+
|
|
164
|
+
if Array === @json_path
|
|
165
|
+
path = build_json_path(@json_path)
|
|
166
|
+
@json_path = path unless frozen?
|
|
167
|
+
return path
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
def build_json_path(segments)
|
|
174
|
+
error = false
|
|
175
|
+
path = segments.filter_map do |segment|
|
|
176
|
+
next if error
|
|
177
|
+
|
|
178
|
+
case segment
|
|
179
|
+
when Integer
|
|
180
|
+
"[#{segment}]"
|
|
181
|
+
when String, Symbol
|
|
182
|
+
if segment.match?(/\A[a-zA-Z\$\_][a-zA-Z\$\_0-9]*\z/)
|
|
183
|
+
".#{segment}"
|
|
184
|
+
else
|
|
185
|
+
segment = segment.to_s.gsub(/["\\]/, { '"' => '\\"', '\\' => '\\\\' })
|
|
186
|
+
%{["#{segment}"]}
|
|
187
|
+
end
|
|
188
|
+
else
|
|
189
|
+
error = true
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
end.join
|
|
193
|
+
"$#{path}".freeze
|
|
194
|
+
end
|
|
250
195
|
end
|
|
251
196
|
|
|
252
197
|
# This exception is raised if the nesting of parsed data structures is too
|
|
@@ -345,19 +290,17 @@ module JSON
|
|
|
345
290
|
# ---
|
|
346
291
|
#
|
|
347
292
|
# Raises an exception if +source+ is not valid JSON:
|
|
348
|
-
# # Raises JSON::ParserError
|
|
349
|
-
# JSON.parse('')
|
|
293
|
+
# # Raises JSON::ParserError unexpected character: 'invalid' at line 1 column 1 :
|
|
294
|
+
# JSON.parse('invalid')
|
|
350
295
|
#
|
|
351
|
-
def parse(source,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
296
|
+
def parse(source, on_load: nil, object_class: nil, array_class: nil, **options)
|
|
297
|
+
if object_class || array_class
|
|
298
|
+
on_load = ParserOptions.on_load(on_load, object_class, array_class)
|
|
299
|
+
end
|
|
355
300
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}.freeze
|
|
360
|
-
private_constant :PARSE_L_OPTIONS
|
|
301
|
+
options[:on_load] = on_load if on_load
|
|
302
|
+
Parser.parse(source, options)
|
|
303
|
+
end
|
|
361
304
|
|
|
362
305
|
# :call-seq:
|
|
363
306
|
# JSON.parse!(source, opts) -> object
|
|
@@ -370,34 +313,30 @@ module JSON
|
|
|
370
313
|
# - Option +max_nesting+, if not provided, defaults to +false+,
|
|
371
314
|
# which disables checking for nesting depth.
|
|
372
315
|
# - Option +allow_nan+, if not provided, defaults to +true+.
|
|
373
|
-
def parse!(source,
|
|
374
|
-
|
|
375
|
-
parse(source, PARSE_L_OPTIONS)
|
|
376
|
-
else
|
|
377
|
-
parse(source, PARSE_L_OPTIONS.merge(opts))
|
|
378
|
-
end
|
|
316
|
+
def parse!(source, **options)
|
|
317
|
+
parse(source, max_nesting: false, allow_nan: true, **options)
|
|
379
318
|
end
|
|
380
319
|
|
|
381
320
|
# :call-seq:
|
|
382
|
-
# JSON.load_file(path,
|
|
321
|
+
# JSON.load_file(path, **) -> object
|
|
383
322
|
#
|
|
384
323
|
# Calls:
|
|
385
|
-
# parse(File.read(path),
|
|
324
|
+
# parse(File.read(path), **)
|
|
386
325
|
#
|
|
387
326
|
# See method #parse.
|
|
388
|
-
def load_file(filespec,
|
|
389
|
-
parse(File.read(filespec, encoding: Encoding::UTF_8),
|
|
327
|
+
def load_file(filespec, ...)
|
|
328
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), ...)
|
|
390
329
|
end
|
|
391
330
|
|
|
392
331
|
# :call-seq:
|
|
393
|
-
# JSON.load_file!(path,
|
|
332
|
+
# JSON.load_file!(path, **)
|
|
394
333
|
#
|
|
395
334
|
# Calls:
|
|
396
|
-
# JSON.parse!(File.read(path,
|
|
335
|
+
# JSON.parse!(File.read(path), **)
|
|
397
336
|
#
|
|
398
337
|
# See method #parse!
|
|
399
|
-
def load_file!(filespec,
|
|
400
|
-
parse!(File.read(filespec, encoding: Encoding::UTF_8),
|
|
338
|
+
def load_file!(filespec, ...)
|
|
339
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), ...)
|
|
401
340
|
end
|
|
402
341
|
|
|
403
342
|
# :call-seq:
|
|
@@ -440,30 +379,8 @@ module JSON
|
|
|
440
379
|
if State === opts
|
|
441
380
|
opts.generate(obj)
|
|
442
381
|
else
|
|
443
|
-
State.generate(obj, opts, nil)
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
# :call-seq:
|
|
448
|
-
# JSON.fast_generate(obj, opts) -> new_string
|
|
449
|
-
#
|
|
450
|
-
# Arguments +obj+ and +opts+ here are the same as
|
|
451
|
-
# arguments +obj+ and +opts+ in JSON.generate.
|
|
452
|
-
#
|
|
453
|
-
# By default, generates \JSON data without checking
|
|
454
|
-
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
|
455
|
-
#
|
|
456
|
-
# Raises an exception if +obj+ contains circular references:
|
|
457
|
-
# a = []; b = []; a.push(b); b.push(a)
|
|
458
|
-
# # Raises SystemStackError (stack level too deep):
|
|
459
|
-
# JSON.fast_generate(a)
|
|
460
|
-
def fast_generate(obj, opts = nil)
|
|
461
|
-
if RUBY_VERSION >= "3.0"
|
|
462
|
-
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
463
|
-
else
|
|
464
|
-
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
382
|
+
State.generate(obj, opts.frozen? ? opts : opts.dup, nil)
|
|
465
383
|
end
|
|
466
|
-
generate(obj, opts)
|
|
467
384
|
end
|
|
468
385
|
|
|
469
386
|
PRETTY_GENERATE_OPTIONS = {
|
|
@@ -519,43 +436,20 @@ module JSON
|
|
|
519
436
|
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
520
437
|
end
|
|
521
438
|
end
|
|
439
|
+
|
|
522
440
|
options = options.merge(opts)
|
|
523
441
|
end
|
|
524
442
|
|
|
525
443
|
State.generate(obj, options, nil)
|
|
526
444
|
end
|
|
527
445
|
|
|
528
|
-
# Sets or returns default options for the JSON.unsafe_load method.
|
|
529
|
-
# Initially:
|
|
530
|
-
# opts = JSON.load_default_options
|
|
531
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
532
|
-
deprecated_singleton_attr_accessor :unsafe_load_default_options
|
|
533
|
-
|
|
534
|
-
@unsafe_load_default_options = {
|
|
535
|
-
:max_nesting => false,
|
|
536
|
-
:allow_nan => true,
|
|
537
|
-
:allow_blank => true,
|
|
538
|
-
:create_additions => true,
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
# Sets or returns default options for the JSON.load method.
|
|
542
|
-
# Initially:
|
|
543
|
-
# opts = JSON.load_default_options
|
|
544
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
545
|
-
deprecated_singleton_attr_accessor :load_default_options
|
|
546
|
-
|
|
547
|
-
@load_default_options = {
|
|
548
|
-
:allow_nan => true,
|
|
549
|
-
:allow_blank => true,
|
|
550
|
-
:create_additions => nil,
|
|
551
|
-
}
|
|
552
446
|
# :call-seq:
|
|
553
447
|
# JSON.unsafe_load(source, options = {}) -> object
|
|
554
448
|
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
|
555
449
|
#
|
|
556
450
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
557
451
|
#
|
|
558
|
-
# BEWARE: This method is meant to
|
|
452
|
+
# BEWARE: This method is meant to deserialise data from trusted user input,
|
|
559
453
|
# like from your own database server or clients under your control, it could
|
|
560
454
|
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
561
455
|
#
|
|
@@ -575,7 +469,6 @@ module JSON
|
|
|
575
469
|
# See details below.
|
|
576
470
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
577
471
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
578
|
-
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
|
579
472
|
#
|
|
580
473
|
# ---
|
|
581
474
|
#
|
|
@@ -680,38 +573,8 @@ module JSON
|
|
|
680
573
|
# #<Admin:0x00000000064c41f8
|
|
681
574
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
682
575
|
#
|
|
683
|
-
def unsafe_load(source, proc = nil, options
|
|
684
|
-
|
|
685
|
-
if proc && proc.is_a?(Hash)
|
|
686
|
-
options, proc = proc, nil
|
|
687
|
-
options
|
|
688
|
-
else
|
|
689
|
-
_unsafe_load_default_options
|
|
690
|
-
end
|
|
691
|
-
else
|
|
692
|
-
_unsafe_load_default_options.merge(options)
|
|
693
|
-
end
|
|
694
|
-
|
|
695
|
-
unless source.is_a?(String)
|
|
696
|
-
if source.respond_to? :to_str
|
|
697
|
-
source = source.to_str
|
|
698
|
-
elsif source.respond_to? :to_io
|
|
699
|
-
source = source.to_io.read
|
|
700
|
-
elsif source.respond_to?(:read)
|
|
701
|
-
source = source.read
|
|
702
|
-
end
|
|
703
|
-
end
|
|
704
|
-
|
|
705
|
-
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
706
|
-
source = 'null'
|
|
707
|
-
end
|
|
708
|
-
|
|
709
|
-
if proc
|
|
710
|
-
opts = opts.dup
|
|
711
|
-
opts[:on_load] = proc.to_proc
|
|
712
|
-
end
|
|
713
|
-
|
|
714
|
-
parse(source, opts)
|
|
576
|
+
def unsafe_load(source, proc = nil, **options)
|
|
577
|
+
load(source, proc, max_nesting: false, **options)
|
|
715
578
|
end
|
|
716
579
|
|
|
717
580
|
# :call-seq:
|
|
@@ -720,16 +583,6 @@ module JSON
|
|
|
720
583
|
#
|
|
721
584
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
722
585
|
#
|
|
723
|
-
# BEWARE: This method is meant to serialise data from trusted user input,
|
|
724
|
-
# like from your own database server or clients under your control, it could
|
|
725
|
-
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
726
|
-
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
|
727
|
-
#
|
|
728
|
-
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
|
|
729
|
-
# non native type is deserialized, without `create_additions` being explicitly
|
|
730
|
-
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
|
|
731
|
-
# by default.
|
|
732
|
-
#
|
|
733
586
|
# - Argument +source+ must be, or be convertible to, a \String:
|
|
734
587
|
# - If +source+ responds to instance method +to_str+,
|
|
735
588
|
# <tt>source.to_str</tt> becomes the source.
|
|
@@ -746,7 +599,6 @@ module JSON
|
|
|
746
599
|
# See details below.
|
|
747
600
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
748
601
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
749
|
-
# The default options can be changed via method JSON.load_default_options=.
|
|
750
602
|
#
|
|
751
603
|
# ---
|
|
752
604
|
#
|
|
@@ -851,23 +703,7 @@ module JSON
|
|
|
851
703
|
# #<Admin:0x00000000064c41f8
|
|
852
704
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
853
705
|
#
|
|
854
|
-
def load(source, proc = nil,
|
|
855
|
-
if proc && options.nil? && proc.is_a?(Hash)
|
|
856
|
-
options = proc
|
|
857
|
-
proc = nil
|
|
858
|
-
end
|
|
859
|
-
|
|
860
|
-
opts = if options.nil?
|
|
861
|
-
if proc && proc.is_a?(Hash)
|
|
862
|
-
options, proc = proc, nil
|
|
863
|
-
options
|
|
864
|
-
else
|
|
865
|
-
_load_default_options
|
|
866
|
-
end
|
|
867
|
-
else
|
|
868
|
-
_load_default_options.merge(options)
|
|
869
|
-
end
|
|
870
|
-
|
|
706
|
+
def load(source, proc = nil, allow_blank: true, **options)
|
|
871
707
|
unless source.is_a?(String)
|
|
872
708
|
if source.respond_to? :to_str
|
|
873
709
|
source = source.to_str
|
|
@@ -878,30 +714,19 @@ module JSON
|
|
|
878
714
|
end
|
|
879
715
|
end
|
|
880
716
|
|
|
881
|
-
if
|
|
717
|
+
if allow_blank && (source.nil? || (String === source && source.empty?))
|
|
882
718
|
source = 'null'
|
|
883
719
|
end
|
|
884
720
|
|
|
885
721
|
if proc
|
|
886
|
-
|
|
887
|
-
|
|
722
|
+
parse(source, allow_nan: true, on_load: proc.to_proc, **options)
|
|
723
|
+
else
|
|
724
|
+
parse(source, allow_nan: true, **options)
|
|
888
725
|
end
|
|
889
|
-
|
|
890
|
-
parse(source, opts)
|
|
891
726
|
end
|
|
892
727
|
|
|
893
|
-
# Sets or returns the default options for the JSON.dump method.
|
|
894
|
-
# Initially:
|
|
895
|
-
# opts = JSON.dump_default_options
|
|
896
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
|
897
|
-
deprecated_singleton_attr_accessor :dump_default_options
|
|
898
|
-
@dump_default_options = {
|
|
899
|
-
:max_nesting => false,
|
|
900
|
-
:allow_nan => true,
|
|
901
|
-
}
|
|
902
|
-
|
|
903
728
|
# :call-seq:
|
|
904
|
-
# JSON.dump(obj, io = nil,
|
|
729
|
+
# JSON.dump(obj, io = nil, options = nil)
|
|
905
730
|
#
|
|
906
731
|
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
|
907
732
|
#
|
|
@@ -910,7 +735,6 @@ module JSON
|
|
|
910
735
|
# - Argument +io+, if given, should respond to method +write+;
|
|
911
736
|
# the \JSON \String is written to +io+, and +io+ is returned.
|
|
912
737
|
# If +io+ is not given, the \JSON \String is returned.
|
|
913
|
-
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
|
914
738
|
#
|
|
915
739
|
# ---
|
|
916
740
|
#
|
|
@@ -927,99 +751,25 @@ module JSON
|
|
|
927
751
|
# puts File.read(path)
|
|
928
752
|
# Output:
|
|
929
753
|
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
|
930
|
-
def dump(obj, anIO = nil,
|
|
754
|
+
def dump(obj, anIO = nil, kwargs = nil)
|
|
931
755
|
if kwargs.nil?
|
|
932
|
-
if
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
anIO = nil
|
|
936
|
-
end
|
|
937
|
-
elsif limit.is_a?(Hash)
|
|
938
|
-
kwargs = limit
|
|
939
|
-
limit = nil
|
|
940
|
-
end
|
|
941
|
-
end
|
|
942
|
-
|
|
943
|
-
unless anIO.nil?
|
|
944
|
-
if anIO.respond_to?(:to_io)
|
|
945
|
-
anIO = anIO.to_io
|
|
946
|
-
elsif limit.nil? && !anIO.respond_to?(:write)
|
|
947
|
-
anIO, limit = nil, anIO
|
|
756
|
+
if anIO.is_a?(Hash)
|
|
757
|
+
kwargs = anIO
|
|
758
|
+
anIO = nil
|
|
948
759
|
end
|
|
949
760
|
end
|
|
950
761
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
opts = opts.merge(kwargs) if kwargs
|
|
954
|
-
|
|
955
|
-
begin
|
|
956
|
-
State.generate(obj, opts, anIO)
|
|
957
|
-
rescue JSON::NestingError
|
|
958
|
-
raise ArgumentError, "exceed depth limit"
|
|
959
|
-
end
|
|
960
|
-
end
|
|
961
|
-
|
|
962
|
-
# :stopdoc:
|
|
963
|
-
# All these were meant to be deprecated circa 2009, but were just set as undocumented
|
|
964
|
-
# so usage still exist in the wild.
|
|
965
|
-
def unparse(...)
|
|
966
|
-
if RUBY_VERSION >= "3.0"
|
|
967
|
-
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
968
|
-
else
|
|
969
|
-
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
970
|
-
end
|
|
971
|
-
generate(...)
|
|
972
|
-
end
|
|
973
|
-
module_function :unparse
|
|
974
|
-
|
|
975
|
-
def fast_unparse(...)
|
|
976
|
-
if RUBY_VERSION >= "3.0"
|
|
977
|
-
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
978
|
-
else
|
|
979
|
-
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
980
|
-
end
|
|
981
|
-
generate(...)
|
|
982
|
-
end
|
|
983
|
-
module_function :fast_unparse
|
|
984
|
-
|
|
985
|
-
def pretty_unparse(...)
|
|
986
|
-
if RUBY_VERSION >= "3.0"
|
|
987
|
-
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
|
|
988
|
-
else
|
|
989
|
-
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
762
|
+
if anIO&.respond_to?(:to_io)
|
|
763
|
+
anIO = anIO.to_io
|
|
990
764
|
end
|
|
991
|
-
pretty_generate(...)
|
|
992
|
-
end
|
|
993
|
-
module_function :fast_unparse
|
|
994
765
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
|
|
1000
|
-
end
|
|
1001
|
-
load(...)
|
|
1002
|
-
end
|
|
1003
|
-
module_function :restore
|
|
766
|
+
opts = {
|
|
767
|
+
allow_nan: true,
|
|
768
|
+
}
|
|
769
|
+
opts.merge!(kwargs) if kwargs
|
|
1004
770
|
|
|
1005
|
-
|
|
1006
|
-
private
|
|
1007
|
-
|
|
1008
|
-
def const_missing(const_name)
|
|
1009
|
-
case const_name
|
|
1010
|
-
when :PRETTY_STATE_PROTOTYPE
|
|
1011
|
-
if RUBY_VERSION >= "3.0"
|
|
1012
|
-
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
|
|
1013
|
-
else
|
|
1014
|
-
warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
1015
|
-
end
|
|
1016
|
-
state.new(PRETTY_GENERATE_OPTIONS)
|
|
1017
|
-
else
|
|
1018
|
-
super
|
|
1019
|
-
end
|
|
1020
|
-
end
|
|
771
|
+
State.generate(obj, opts, anIO)
|
|
1021
772
|
end
|
|
1022
|
-
# :startdoc:
|
|
1023
773
|
|
|
1024
774
|
# JSON::Coder holds a parser and generator configuration.
|
|
1025
775
|
#
|
|
@@ -1032,11 +782,33 @@ module JSON
|
|
|
1032
782
|
# MyApp::JSONC_CODER.load(document)
|
|
1033
783
|
#
|
|
1034
784
|
class Coder
|
|
785
|
+
PARSER_OPTIONS = %i(
|
|
786
|
+
max_nesting
|
|
787
|
+
allow_nan
|
|
788
|
+
allow_trailing_comma
|
|
789
|
+
allow_comments
|
|
790
|
+
allow_control_characters
|
|
791
|
+
allow_invalid_escape
|
|
792
|
+
symbolize_names
|
|
793
|
+
freeze
|
|
794
|
+
allow_duplicate_key
|
|
795
|
+
decimal_class
|
|
796
|
+
).freeze
|
|
797
|
+
private_constant :PARSER_OPTIONS
|
|
798
|
+
|
|
799
|
+
EXCLUDED_GENERATOR_OPTIONS = (PARSER_OPTIONS - %i(
|
|
800
|
+
max_nesting
|
|
801
|
+
allow_nan
|
|
802
|
+
allow_duplicate_key
|
|
803
|
+
)).freeze
|
|
804
|
+
private_constant :EXCLUDED_GENERATOR_OPTIONS
|
|
805
|
+
|
|
1035
806
|
# :call-seq:
|
|
1036
807
|
# JSON.new(options = nil, &block)
|
|
1037
808
|
#
|
|
1038
809
|
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
|
1039
|
-
# See {Parsing Options}[
|
|
810
|
+
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
811
|
+
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
1040
812
|
#
|
|
1041
813
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1042
814
|
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
@@ -1055,17 +827,20 @@ module JSON
|
|
|
1055
827
|
#
|
|
1056
828
|
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
1057
829
|
#
|
|
1058
|
-
def initialize(
|
|
1059
|
-
if
|
|
1060
|
-
|
|
1061
|
-
else
|
|
1062
|
-
options = options.dup
|
|
1063
|
-
options[:strict] = true
|
|
830
|
+
def initialize(object_class: nil, array_class: nil, on_load: nil, **options, &as_json)
|
|
831
|
+
if object_class || array_class
|
|
832
|
+
on_load = ParserOptions.on_load(on_load, object_class, array_class)
|
|
1064
833
|
end
|
|
1065
|
-
|
|
834
|
+
parser_options = options.slice(*PARSER_OPTIONS)
|
|
835
|
+
parser_options[:on_load] = on_load if on_load
|
|
836
|
+
@parser_config = Ext::Parser::Config.new(parser_options).freeze
|
|
1066
837
|
|
|
1067
|
-
|
|
1068
|
-
@
|
|
838
|
+
generator_options = options.reject { |k, _| EXCLUDED_GENERATOR_OPTIONS.include?(k) }
|
|
839
|
+
@state = State.new(
|
|
840
|
+
**generator_options,
|
|
841
|
+
strict: true,
|
|
842
|
+
as_json: as_json,
|
|
843
|
+
).freeze
|
|
1069
844
|
end
|
|
1070
845
|
|
|
1071
846
|
# call-seq:
|
|
@@ -1095,40 +870,34 @@ module JSON
|
|
|
1095
870
|
load(File.read(path, encoding: Encoding::UTF_8))
|
|
1096
871
|
end
|
|
1097
872
|
end
|
|
1098
|
-
end
|
|
1099
873
|
|
|
1100
|
-
module
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
874
|
+
module GeneratorMethods
|
|
875
|
+
# call-seq: to_json(*)
|
|
876
|
+
#
|
|
877
|
+
# Converts this object into a JSON string.
|
|
878
|
+
# If this object doesn't directly maps to a JSON native type,
|
|
879
|
+
# first convert it to a string (calling #to_s), then converts
|
|
880
|
+
# it to a JSON string, and returns the result.
|
|
881
|
+
# This is a fallback, if no special method #to_json was defined for some object.
|
|
882
|
+
def to_json(state = nil, *)
|
|
883
|
+
obj = case self
|
|
884
|
+
when nil, false, true, Integer, Float, Array, Hash
|
|
885
|
+
self
|
|
886
|
+
else
|
|
887
|
+
"#{self}"
|
|
888
|
+
end
|
|
1111
889
|
|
|
1112
|
-
|
|
1113
|
-
|
|
890
|
+
if state.nil?
|
|
891
|
+
JSON::State._generate_no_fallback(obj, nil, nil)
|
|
892
|
+
else
|
|
893
|
+
JSON::State.from_state(state)._generate_no_fallback(obj)
|
|
894
|
+
end
|
|
1114
895
|
end
|
|
1115
|
-
nil
|
|
1116
896
|
end
|
|
897
|
+
end
|
|
1117
898
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
def jj(*objs)
|
|
1121
|
-
if RUBY_VERSION >= "3.0"
|
|
1122
|
-
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
|
|
1123
|
-
else
|
|
1124
|
-
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
|
|
1125
|
-
end
|
|
1126
|
-
|
|
1127
|
-
objs.each do |obj|
|
|
1128
|
-
puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
|
|
1129
|
-
end
|
|
1130
|
-
nil
|
|
1131
|
-
end
|
|
899
|
+
module ::Kernel
|
|
900
|
+
private
|
|
1132
901
|
|
|
1133
902
|
# If _object_ is string-like, parse the string and return the parsed result as
|
|
1134
903
|
# a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
|
|
@@ -1140,3 +909,7 @@ module ::Kernel
|
|
|
1140
909
|
JSON[object, opts]
|
|
1141
910
|
end
|
|
1142
911
|
end
|
|
912
|
+
|
|
913
|
+
class Object
|
|
914
|
+
include JSON::GeneratorMethods
|
|
915
|
+
end
|