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