json 2.12.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.
- checksums.yaml +4 -4
- data/CHANGES.md +222 -8
- data/LEGAL +12 -0
- data/README.md +34 -87
- data/ext/json/ext/fbuffer/fbuffer.h +75 -86
- data/ext/json/ext/generator/extconf.rb +5 -26
- data/ext/json/ext/generator/generator.c +560 -688
- data/ext/json/ext/json.h +196 -0
- data/ext/json/ext/parser/extconf.rb +34 -2
- data/ext/json/ext/parser/parser.c +2177 -673
- data/ext/json/ext/simd/conf.rb +24 -0
- data/ext/json/ext/simd/simd.h +208 -0
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/ext/json/ext/vendor/fpconv.c +13 -12
- data/json.gemspec +2 -3
- data/lib/json/common.rb +200 -389
- data/lib/json/ext/generator/state.rb +8 -37
- data/lib/json/ext.rb +25 -1
- data/lib/json/truffle_ruby/generator.rb +186 -128
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +107 -242
- metadata +7 -19
- data/ext/json/ext/generator/simd.h +0 -112
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -12
- 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/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 -75
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,70 +34,11 @@ module JSON
|
|
|
47
34
|
on_load.nil? ? obj : on_load.call(obj)
|
|
48
35
|
end
|
|
49
36
|
end
|
|
50
|
-
|
|
51
|
-
# TODO: exctract :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_name = object[JSON.create_id]
|
|
75
|
-
klass = JSON.deep_const_get(class_name)
|
|
76
|
-
if (klass.respond_to?(:json_creatable?) && klass.json_creatable?) || klass.respond_to?(:json_create)
|
|
77
|
-
create_additions_warning if create_additions.nil?
|
|
78
|
-
object = klass.json_create(object)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
on_load.nil? ? object : on_load.call(object)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
opts
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
GEM_ROOT = File.expand_path("../../../", __FILE__) + "/"
|
|
91
|
-
def create_additions_warning
|
|
92
|
-
message = "JSON.load implicit support for `create_additions: true` is deprecated " \
|
|
93
|
-
"and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
|
|
94
|
-
"pass `create_additions: true`"
|
|
95
|
-
|
|
96
|
-
uplevel = 4
|
|
97
|
-
caller_locations(uplevel, 10).each do |frame|
|
|
98
|
-
if frame.path.nil? || frame.path.start_with?(GEM_ROOT) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
|
|
99
|
-
uplevel += 1
|
|
100
|
-
else
|
|
101
|
-
break
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
if RUBY_VERSION >= "3.0"
|
|
106
|
-
warn(message, uplevel: uplevel - 1, category: :deprecated)
|
|
107
|
-
else
|
|
108
|
-
warn(message, uplevel: uplevel - 1)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
37
|
end
|
|
112
38
|
end
|
|
113
39
|
|
|
40
|
+
private_constant :ParserOptions
|
|
41
|
+
|
|
114
42
|
class << self
|
|
115
43
|
# :call-seq:
|
|
116
44
|
# JSON[object] -> new_array or new_string
|
|
@@ -124,12 +52,14 @@ module JSON
|
|
|
124
52
|
# ruby = [0, 1, nil]
|
|
125
53
|
# JSON[ruby] # => '[0,1,null]'
|
|
126
54
|
def [](object, opts = nil)
|
|
55
|
+
opts ||= {}.freeze
|
|
56
|
+
|
|
127
57
|
if object.is_a?(String)
|
|
128
|
-
return JSON.parse(object, opts)
|
|
58
|
+
return JSON.parse(object, **opts)
|
|
129
59
|
elsif object.respond_to?(:to_str)
|
|
130
60
|
str = object.to_str
|
|
131
61
|
if str.is_a?(String)
|
|
132
|
-
return JSON.parse(str, opts)
|
|
62
|
+
return JSON.parse(str, **opts)
|
|
133
63
|
end
|
|
134
64
|
end
|
|
135
65
|
|
|
@@ -146,29 +76,30 @@ module JSON
|
|
|
146
76
|
const_set :Parser, parser
|
|
147
77
|
end
|
|
148
78
|
|
|
149
|
-
# Return the constant located at _path_. The format of _path_ has to be
|
|
150
|
-
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
|
|
151
|
-
# level (absolute namespace path?). If there doesn't exist a constant at
|
|
152
|
-
# the given path, an ArgumentError is raised.
|
|
153
|
-
def deep_const_get(path) # :nodoc:
|
|
154
|
-
Object.const_get(path)
|
|
155
|
-
rescue NameError => e
|
|
156
|
-
raise ArgumentError, "can't get const #{path}: #{e}"
|
|
157
|
-
end
|
|
158
|
-
|
|
159
79
|
# Set the module _generator_ to be used by JSON.
|
|
160
80
|
def generator=(generator) # :nodoc:
|
|
161
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
|
+
|
|
162
91
|
@generator = generator
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
|
170
102
|
end
|
|
171
|
-
include modul
|
|
172
103
|
end
|
|
173
104
|
end
|
|
174
105
|
self.state = generator::State
|
|
@@ -185,41 +116,21 @@ module JSON
|
|
|
185
116
|
|
|
186
117
|
private
|
|
187
118
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
|
|
194
|
-
@#{attr}
|
|
195
|
-
end
|
|
119
|
+
# Called from the extension when a hash has both string and symbol keys
|
|
120
|
+
def on_mixed_keys_hash(hash)
|
|
121
|
+
set = {}
|
|
122
|
+
hash.each_key do |key|
|
|
123
|
+
key_str = key.to_s
|
|
196
124
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def _#{attr}
|
|
203
|
-
@#{attr}
|
|
204
|
-
end
|
|
205
|
-
RUBY
|
|
125
|
+
if set[key_str]
|
|
126
|
+
raise GeneratorError, "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
|
|
127
|
+
else
|
|
128
|
+
set[key_str] = true
|
|
129
|
+
end
|
|
206
130
|
end
|
|
207
131
|
end
|
|
208
132
|
end
|
|
209
133
|
|
|
210
|
-
# Sets create identifier, which is used to decide if the _json_create_
|
|
211
|
-
# hook of a class should be called; initial value is +json_class+:
|
|
212
|
-
# JSON.create_id # => 'json_class'
|
|
213
|
-
def self.create_id=(new_value)
|
|
214
|
-
Thread.current[:"JSON.create_id"] = new_value.dup.freeze
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# Returns the current create identifier.
|
|
218
|
-
# See also JSON.create_id=.
|
|
219
|
-
def self.create_id
|
|
220
|
-
Thread.current[:"JSON.create_id"] || 'json_class'
|
|
221
|
-
end
|
|
222
|
-
|
|
223
134
|
NaN = Float::NAN
|
|
224
135
|
|
|
225
136
|
Infinity = Float::INFINITY
|
|
@@ -231,7 +142,56 @@ module JSON
|
|
|
231
142
|
|
|
232
143
|
# This exception is raised if a parser error occurs.
|
|
233
144
|
class ParserError < JSONError
|
|
234
|
-
|
|
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
|
|
235
195
|
end
|
|
236
196
|
|
|
237
197
|
# This exception is raised if the nesting of parsed data structures is too
|
|
@@ -268,7 +228,7 @@ module JSON
|
|
|
268
228
|
# to string interpolation.
|
|
269
229
|
#
|
|
270
230
|
# Note: no validation is performed on the provided string. It is the
|
|
271
|
-
#
|
|
231
|
+
# responsibility of the caller to ensure the string contains valid JSON.
|
|
272
232
|
Fragment = Struct.new(:json) do
|
|
273
233
|
def initialize(json)
|
|
274
234
|
unless string = String.try_convert(json)
|
|
@@ -330,19 +290,17 @@ module JSON
|
|
|
330
290
|
# ---
|
|
331
291
|
#
|
|
332
292
|
# Raises an exception if +source+ is not valid JSON:
|
|
333
|
-
# # Raises JSON::ParserError
|
|
334
|
-
# JSON.parse('')
|
|
293
|
+
# # Raises JSON::ParserError unexpected character: 'invalid' at line 1 column 1 :
|
|
294
|
+
# JSON.parse('invalid')
|
|
335
295
|
#
|
|
336
|
-
def parse(source,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
|
340
300
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}.freeze
|
|
345
|
-
private_constant :PARSE_L_OPTIONS
|
|
301
|
+
options[:on_load] = on_load if on_load
|
|
302
|
+
Parser.parse(source, options)
|
|
303
|
+
end
|
|
346
304
|
|
|
347
305
|
# :call-seq:
|
|
348
306
|
# JSON.parse!(source, opts) -> object
|
|
@@ -355,34 +313,30 @@ module JSON
|
|
|
355
313
|
# - Option +max_nesting+, if not provided, defaults to +false+,
|
|
356
314
|
# which disables checking for nesting depth.
|
|
357
315
|
# - Option +allow_nan+, if not provided, defaults to +true+.
|
|
358
|
-
def parse!(source,
|
|
359
|
-
|
|
360
|
-
parse(source, PARSE_L_OPTIONS)
|
|
361
|
-
else
|
|
362
|
-
parse(source, PARSE_L_OPTIONS.merge(opts))
|
|
363
|
-
end
|
|
316
|
+
def parse!(source, **options)
|
|
317
|
+
parse(source, max_nesting: false, allow_nan: true, **options)
|
|
364
318
|
end
|
|
365
319
|
|
|
366
320
|
# :call-seq:
|
|
367
|
-
# JSON.load_file(path,
|
|
321
|
+
# JSON.load_file(path, **) -> object
|
|
368
322
|
#
|
|
369
323
|
# Calls:
|
|
370
|
-
# parse(File.read(path),
|
|
324
|
+
# parse(File.read(path), **)
|
|
371
325
|
#
|
|
372
326
|
# See method #parse.
|
|
373
|
-
def load_file(filespec,
|
|
374
|
-
parse(File.read(filespec, encoding: Encoding::UTF_8),
|
|
327
|
+
def load_file(filespec, ...)
|
|
328
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), ...)
|
|
375
329
|
end
|
|
376
330
|
|
|
377
331
|
# :call-seq:
|
|
378
|
-
# JSON.load_file!(path,
|
|
332
|
+
# JSON.load_file!(path, **)
|
|
379
333
|
#
|
|
380
334
|
# Calls:
|
|
381
|
-
# JSON.parse!(File.read(path,
|
|
335
|
+
# JSON.parse!(File.read(path), **)
|
|
382
336
|
#
|
|
383
337
|
# See method #parse!
|
|
384
|
-
def load_file!(filespec,
|
|
385
|
-
parse!(File.read(filespec, encoding: Encoding::UTF_8),
|
|
338
|
+
def load_file!(filespec, ...)
|
|
339
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), ...)
|
|
386
340
|
end
|
|
387
341
|
|
|
388
342
|
# :call-seq:
|
|
@@ -390,7 +344,7 @@ module JSON
|
|
|
390
344
|
#
|
|
391
345
|
# Returns a \String containing the generated \JSON data.
|
|
392
346
|
#
|
|
393
|
-
# See also JSON.
|
|
347
|
+
# See also JSON.pretty_generate.
|
|
394
348
|
#
|
|
395
349
|
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
|
396
350
|
#
|
|
@@ -425,32 +379,10 @@ module JSON
|
|
|
425
379
|
if State === opts
|
|
426
380
|
opts.generate(obj)
|
|
427
381
|
else
|
|
428
|
-
State.generate(obj, opts, nil)
|
|
382
|
+
State.generate(obj, opts.frozen? ? opts : opts.dup, nil)
|
|
429
383
|
end
|
|
430
384
|
end
|
|
431
385
|
|
|
432
|
-
# :call-seq:
|
|
433
|
-
# JSON.fast_generate(obj, opts) -> new_string
|
|
434
|
-
#
|
|
435
|
-
# Arguments +obj+ and +opts+ here are the same as
|
|
436
|
-
# arguments +obj+ and +opts+ in JSON.generate.
|
|
437
|
-
#
|
|
438
|
-
# By default, generates \JSON data without checking
|
|
439
|
-
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
|
440
|
-
#
|
|
441
|
-
# Raises an exception if +obj+ contains circular references:
|
|
442
|
-
# a = []; b = []; a.push(b); b.push(a)
|
|
443
|
-
# # Raises SystemStackError (stack level too deep):
|
|
444
|
-
# JSON.fast_generate(a)
|
|
445
|
-
def fast_generate(obj, opts = nil)
|
|
446
|
-
if RUBY_VERSION >= "3.0"
|
|
447
|
-
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
448
|
-
else
|
|
449
|
-
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
450
|
-
end
|
|
451
|
-
generate(obj, opts)
|
|
452
|
-
end
|
|
453
|
-
|
|
454
386
|
PRETTY_GENERATE_OPTIONS = {
|
|
455
387
|
indent: ' ',
|
|
456
388
|
space: ' ',
|
|
@@ -504,42 +436,20 @@ module JSON
|
|
|
504
436
|
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
505
437
|
end
|
|
506
438
|
end
|
|
439
|
+
|
|
507
440
|
options = options.merge(opts)
|
|
508
441
|
end
|
|
509
442
|
|
|
510
443
|
State.generate(obj, options, nil)
|
|
511
444
|
end
|
|
512
445
|
|
|
513
|
-
# Sets or returns default options for the JSON.unsafe_load method.
|
|
514
|
-
# Initially:
|
|
515
|
-
# opts = JSON.load_default_options
|
|
516
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
517
|
-
deprecated_singleton_attr_accessor :unsafe_load_default_options
|
|
518
|
-
|
|
519
|
-
@unsafe_load_default_options = {
|
|
520
|
-
:max_nesting => false,
|
|
521
|
-
:allow_nan => true,
|
|
522
|
-
:allow_blank => true,
|
|
523
|
-
:create_additions => true,
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
# Sets or returns default options for the JSON.load method.
|
|
527
|
-
# Initially:
|
|
528
|
-
# opts = JSON.load_default_options
|
|
529
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
530
|
-
deprecated_singleton_attr_accessor :load_default_options
|
|
531
|
-
|
|
532
|
-
@load_default_options = {
|
|
533
|
-
:allow_nan => true,
|
|
534
|
-
:allow_blank => true,
|
|
535
|
-
:create_additions => nil,
|
|
536
|
-
}
|
|
537
446
|
# :call-seq:
|
|
447
|
+
# JSON.unsafe_load(source, options = {}) -> object
|
|
538
448
|
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
|
539
449
|
#
|
|
540
450
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
541
451
|
#
|
|
542
|
-
# BEWARE: This method is meant to
|
|
452
|
+
# BEWARE: This method is meant to deserialise data from trusted user input,
|
|
543
453
|
# like from your own database server or clients under your control, it could
|
|
544
454
|
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
545
455
|
#
|
|
@@ -559,7 +469,6 @@ module JSON
|
|
|
559
469
|
# See details below.
|
|
560
470
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
561
471
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
562
|
-
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
|
563
472
|
#
|
|
564
473
|
# ---
|
|
565
474
|
#
|
|
@@ -642,6 +551,7 @@ module JSON
|
|
|
642
551
|
# when Array
|
|
643
552
|
# obj.map! {|v| deserialize_obj v }
|
|
644
553
|
# end
|
|
554
|
+
# obj
|
|
645
555
|
# })
|
|
646
556
|
# pp ruby
|
|
647
557
|
# Output:
|
|
@@ -663,46 +573,16 @@ module JSON
|
|
|
663
573
|
# #<Admin:0x00000000064c41f8
|
|
664
574
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
665
575
|
#
|
|
666
|
-
def unsafe_load(source, proc = nil, options
|
|
667
|
-
|
|
668
|
-
_unsafe_load_default_options
|
|
669
|
-
else
|
|
670
|
-
_unsafe_load_default_options.merge(options)
|
|
671
|
-
end
|
|
672
|
-
|
|
673
|
-
unless source.is_a?(String)
|
|
674
|
-
if source.respond_to? :to_str
|
|
675
|
-
source = source.to_str
|
|
676
|
-
elsif source.respond_to? :to_io
|
|
677
|
-
source = source.to_io.read
|
|
678
|
-
elsif source.respond_to?(:read)
|
|
679
|
-
source = source.read
|
|
680
|
-
end
|
|
681
|
-
end
|
|
682
|
-
|
|
683
|
-
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
684
|
-
source = 'null'
|
|
685
|
-
end
|
|
686
|
-
result = parse(source, opts)
|
|
687
|
-
recurse_proc(result, &proc) if proc
|
|
688
|
-
result
|
|
576
|
+
def unsafe_load(source, proc = nil, **options)
|
|
577
|
+
load(source, proc, max_nesting: false, **options)
|
|
689
578
|
end
|
|
690
579
|
|
|
691
580
|
# :call-seq:
|
|
581
|
+
# JSON.load(source, options = {}) -> object
|
|
692
582
|
# JSON.load(source, proc = nil, options = {}) -> object
|
|
693
583
|
#
|
|
694
584
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
695
585
|
#
|
|
696
|
-
# BEWARE: This method is meant to serialise data from trusted user input,
|
|
697
|
-
# like from your own database server or clients under your control, it could
|
|
698
|
-
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
699
|
-
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
|
700
|
-
#
|
|
701
|
-
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
|
|
702
|
-
# non native type is deserialized, without `create_additions` being explicitly
|
|
703
|
-
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
|
|
704
|
-
# by default.
|
|
705
|
-
#
|
|
706
586
|
# - Argument +source+ must be, or be convertible to, a \String:
|
|
707
587
|
# - If +source+ responds to instance method +to_str+,
|
|
708
588
|
# <tt>source.to_str</tt> becomes the source.
|
|
@@ -719,7 +599,6 @@ module JSON
|
|
|
719
599
|
# See details below.
|
|
720
600
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
721
601
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
722
|
-
# The default options can be changed via method JSON.load_default_options=.
|
|
723
602
|
#
|
|
724
603
|
# ---
|
|
725
604
|
#
|
|
@@ -802,6 +681,7 @@ module JSON
|
|
|
802
681
|
# when Array
|
|
803
682
|
# obj.map! {|v| deserialize_obj v }
|
|
804
683
|
# end
|
|
684
|
+
# obj
|
|
805
685
|
# })
|
|
806
686
|
# pp ruby
|
|
807
687
|
# Output:
|
|
@@ -823,13 +703,7 @@ module JSON
|
|
|
823
703
|
# #<Admin:0x00000000064c41f8
|
|
824
704
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
825
705
|
#
|
|
826
|
-
def load(source, proc = nil,
|
|
827
|
-
opts = if options.nil?
|
|
828
|
-
_load_default_options
|
|
829
|
-
else
|
|
830
|
-
_load_default_options.merge(options)
|
|
831
|
-
end
|
|
832
|
-
|
|
706
|
+
def load(source, proc = nil, allow_blank: true, **options)
|
|
833
707
|
unless source.is_a?(String)
|
|
834
708
|
if source.respond_to? :to_str
|
|
835
709
|
source = source.to_str
|
|
@@ -840,30 +714,19 @@ module JSON
|
|
|
840
714
|
end
|
|
841
715
|
end
|
|
842
716
|
|
|
843
|
-
if
|
|
717
|
+
if allow_blank && (source.nil? || (String === source && source.empty?))
|
|
844
718
|
source = 'null'
|
|
845
719
|
end
|
|
846
720
|
|
|
847
721
|
if proc
|
|
848
|
-
|
|
849
|
-
|
|
722
|
+
parse(source, allow_nan: true, on_load: proc.to_proc, **options)
|
|
723
|
+
else
|
|
724
|
+
parse(source, allow_nan: true, **options)
|
|
850
725
|
end
|
|
851
|
-
|
|
852
|
-
parse(source, opts)
|
|
853
726
|
end
|
|
854
727
|
|
|
855
|
-
# Sets or returns the default options for the JSON.dump method.
|
|
856
|
-
# Initially:
|
|
857
|
-
# opts = JSON.dump_default_options
|
|
858
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
|
859
|
-
deprecated_singleton_attr_accessor :dump_default_options
|
|
860
|
-
@dump_default_options = {
|
|
861
|
-
:max_nesting => false,
|
|
862
|
-
:allow_nan => true,
|
|
863
|
-
}
|
|
864
|
-
|
|
865
728
|
# :call-seq:
|
|
866
|
-
# JSON.dump(obj, io = nil,
|
|
729
|
+
# JSON.dump(obj, io = nil, options = nil)
|
|
867
730
|
#
|
|
868
731
|
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
|
869
732
|
#
|
|
@@ -872,7 +735,6 @@ module JSON
|
|
|
872
735
|
# - Argument +io+, if given, should respond to method +write+;
|
|
873
736
|
# the \JSON \String is written to +io+, and +io+ is returned.
|
|
874
737
|
# If +io+ is not given, the \JSON \String is returned.
|
|
875
|
-
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
|
876
738
|
#
|
|
877
739
|
# ---
|
|
878
740
|
#
|
|
@@ -889,99 +751,25 @@ module JSON
|
|
|
889
751
|
# puts File.read(path)
|
|
890
752
|
# Output:
|
|
891
753
|
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
|
892
|
-
def dump(obj, anIO = nil,
|
|
754
|
+
def dump(obj, anIO = nil, kwargs = nil)
|
|
893
755
|
if kwargs.nil?
|
|
894
|
-
if
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
anIO = nil
|
|
898
|
-
end
|
|
899
|
-
elsif limit.is_a?(Hash)
|
|
900
|
-
kwargs = limit
|
|
901
|
-
limit = nil
|
|
756
|
+
if anIO.is_a?(Hash)
|
|
757
|
+
kwargs = anIO
|
|
758
|
+
anIO = nil
|
|
902
759
|
end
|
|
903
760
|
end
|
|
904
761
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
anIO = anIO.to_io
|
|
908
|
-
elsif limit.nil? && !anIO.respond_to?(:write)
|
|
909
|
-
anIO, limit = nil, anIO
|
|
910
|
-
end
|
|
911
|
-
end
|
|
912
|
-
|
|
913
|
-
opts = JSON._dump_default_options
|
|
914
|
-
opts = opts.merge(:max_nesting => limit) if limit
|
|
915
|
-
opts = opts.merge(kwargs) if kwargs
|
|
916
|
-
|
|
917
|
-
begin
|
|
918
|
-
State.generate(obj, opts, anIO)
|
|
919
|
-
rescue JSON::NestingError
|
|
920
|
-
raise ArgumentError, "exceed depth limit"
|
|
921
|
-
end
|
|
922
|
-
end
|
|
923
|
-
|
|
924
|
-
# :stopdoc:
|
|
925
|
-
# All these were meant to be deprecated circa 2009, but were just set as undocumented
|
|
926
|
-
# so usage still exist in the wild.
|
|
927
|
-
def unparse(...)
|
|
928
|
-
if RUBY_VERSION >= "3.0"
|
|
929
|
-
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
930
|
-
else
|
|
931
|
-
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
932
|
-
end
|
|
933
|
-
generate(...)
|
|
934
|
-
end
|
|
935
|
-
module_function :unparse
|
|
936
|
-
|
|
937
|
-
def fast_unparse(...)
|
|
938
|
-
if RUBY_VERSION >= "3.0"
|
|
939
|
-
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
940
|
-
else
|
|
941
|
-
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
942
|
-
end
|
|
943
|
-
generate(...)
|
|
944
|
-
end
|
|
945
|
-
module_function :fast_unparse
|
|
946
|
-
|
|
947
|
-
def pretty_unparse(...)
|
|
948
|
-
if RUBY_VERSION >= "3.0"
|
|
949
|
-
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
|
|
950
|
-
else
|
|
951
|
-
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
952
|
-
end
|
|
953
|
-
pretty_generate(...)
|
|
954
|
-
end
|
|
955
|
-
module_function :fast_unparse
|
|
956
|
-
|
|
957
|
-
def restore(...)
|
|
958
|
-
if RUBY_VERSION >= "3.0"
|
|
959
|
-
warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
|
|
960
|
-
else
|
|
961
|
-
warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
|
|
762
|
+
if anIO&.respond_to?(:to_io)
|
|
763
|
+
anIO = anIO.to_io
|
|
962
764
|
end
|
|
963
|
-
load(...)
|
|
964
|
-
end
|
|
965
|
-
module_function :restore
|
|
966
765
|
|
|
967
|
-
|
|
968
|
-
|
|
766
|
+
opts = {
|
|
767
|
+
allow_nan: true,
|
|
768
|
+
}
|
|
769
|
+
opts.merge!(kwargs) if kwargs
|
|
969
770
|
|
|
970
|
-
|
|
971
|
-
case const_name
|
|
972
|
-
when :PRETTY_STATE_PROTOTYPE
|
|
973
|
-
if RUBY_VERSION >= "3.0"
|
|
974
|
-
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
|
|
975
|
-
else
|
|
976
|
-
warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
977
|
-
end
|
|
978
|
-
state.new(PRETTY_GENERATE_OPTIONS)
|
|
979
|
-
else
|
|
980
|
-
super
|
|
981
|
-
end
|
|
982
|
-
end
|
|
771
|
+
State.generate(obj, opts, anIO)
|
|
983
772
|
end
|
|
984
|
-
# :startdoc:
|
|
985
773
|
|
|
986
774
|
# JSON::Coder holds a parser and generator configuration.
|
|
987
775
|
#
|
|
@@ -994,14 +782,36 @@ module JSON
|
|
|
994
782
|
# MyApp::JSONC_CODER.load(document)
|
|
995
783
|
#
|
|
996
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
|
+
|
|
997
806
|
# :call-seq:
|
|
998
807
|
# JSON.new(options = nil, &block)
|
|
999
808
|
#
|
|
1000
809
|
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
|
1001
|
-
# See {Parsing Options}[
|
|
810
|
+
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
811
|
+
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
1002
812
|
#
|
|
1003
813
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1004
|
-
#
|
|
814
|
+
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
1005
815
|
# \JSON counterpart:
|
|
1006
816
|
#
|
|
1007
817
|
# module MyApp
|
|
@@ -1017,17 +827,20 @@ module JSON
|
|
|
1017
827
|
#
|
|
1018
828
|
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
1019
829
|
#
|
|
1020
|
-
def initialize(
|
|
1021
|
-
if
|
|
1022
|
-
|
|
1023
|
-
else
|
|
1024
|
-
options = options.dup
|
|
1025
|
-
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)
|
|
1026
833
|
end
|
|
1027
|
-
|
|
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
|
|
1028
837
|
|
|
1029
|
-
|
|
1030
|
-
@
|
|
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
|
|
1031
844
|
end
|
|
1032
845
|
|
|
1033
846
|
# call-seq:
|
|
@@ -1036,7 +849,7 @@ module JSON
|
|
|
1036
849
|
#
|
|
1037
850
|
# Serialize the given object into a \JSON document.
|
|
1038
851
|
def dump(object, io = nil)
|
|
1039
|
-
@state.
|
|
852
|
+
@state.generate(object, io)
|
|
1040
853
|
end
|
|
1041
854
|
alias_method :generate, :dump
|
|
1042
855
|
|
|
@@ -1057,40 +870,34 @@ module JSON
|
|
|
1057
870
|
load(File.read(path, encoding: Encoding::UTF_8))
|
|
1058
871
|
end
|
|
1059
872
|
end
|
|
1060
|
-
end
|
|
1061
|
-
|
|
1062
|
-
module ::Kernel
|
|
1063
|
-
private
|
|
1064
873
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
|
1073
889
|
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
|
1076
895
|
end
|
|
1077
|
-
nil
|
|
1078
896
|
end
|
|
897
|
+
end
|
|
1079
898
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
def jj(*objs)
|
|
1083
|
-
if RUBY_VERSION >= "3.0"
|
|
1084
|
-
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
|
|
1085
|
-
else
|
|
1086
|
-
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
|
-
objs.each do |obj|
|
|
1090
|
-
puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
|
|
1091
|
-
end
|
|
1092
|
-
nil
|
|
1093
|
-
end
|
|
899
|
+
module ::Kernel
|
|
900
|
+
private
|
|
1094
901
|
|
|
1095
902
|
# If _object_ is string-like, parse the string and return the parsed result as
|
|
1096
903
|
# a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
|
|
@@ -1102,3 +909,7 @@ module ::Kernel
|
|
|
1102
909
|
JSON[object, opts]
|
|
1103
910
|
end
|
|
1104
911
|
end
|
|
912
|
+
|
|
913
|
+
class Object
|
|
914
|
+
include JSON::GeneratorMethods
|
|
915
|
+
end
|