json 2.6.1 → 2.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/BSDL +22 -0
- data/CHANGES.md +370 -17
- data/LEGAL +20 -0
- data/README.md +96 -213
- data/ext/json/ext/fbuffer/fbuffer.h +190 -118
- data/ext/json/ext/generator/extconf.rb +17 -2
- data/ext/json/ext/generator/generator.c +1532 -1074
- data/ext/json/ext/json.h +183 -0
- data/ext/json/ext/parser/extconf.rb +30 -25
- data/ext/json/ext/parser/parser.c +2837 -3258
- 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 +480 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/json.gemspec +48 -53
- data/lib/json/add/bigdecimal.rb +39 -10
- data/lib/json/add/complex.rb +29 -6
- data/lib/json/add/core.rb +2 -1
- data/lib/json/add/date.rb +27 -7
- data/lib/json/add/date_time.rb +26 -9
- data/lib/json/add/exception.rb +25 -7
- data/lib/json/add/ostruct.rb +32 -9
- data/lib/json/add/range.rb +33 -8
- data/lib/json/add/rational.rb +28 -6
- data/lib/json/add/regexp.rb +26 -8
- data/lib/json/add/set.rb +25 -6
- data/lib/json/add/string.rb +35 -0
- data/lib/json/add/struct.rb +29 -7
- data/lib/json/add/symbol.rb +34 -7
- data/lib/json/add/time.rb +29 -15
- data/lib/json/common.rb +746 -267
- data/lib/json/ext/generator/state.rb +104 -0
- data/lib/json/ext.rb +61 -5
- data/lib/json/generic_object.rb +7 -11
- data/lib/json/truffle_ruby/generator.rb +790 -0
- data/lib/json/version.rb +3 -7
- data/lib/json.rb +134 -24
- metadata +22 -26
- data/VERSION +0 -1
- data/ext/json/ext/generator/depend +0 -1
- data/ext/json/ext/generator/generator.h +0 -174
- data/ext/json/ext/parser/depend +0 -1
- data/ext/json/ext/parser/parser.h +0 -96
- data/ext/json/ext/parser/parser.rl +0 -977
- data/ext/json/extconf.rb +0 -3
- data/lib/json/pure/generator.rb +0 -479
- data/lib/json/pure/parser.rb +0 -337
- data/lib/json/pure.rb +0 -15
- /data/{LICENSE → COPYING} +0 -0
data/lib/json/common.rb
CHANGED
|
@@ -1,9 +1,123 @@
|
|
|
1
|
-
#frozen_string_literal:
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'json/version'
|
|
3
|
-
require 'json/generic_object'
|
|
4
4
|
|
|
5
5
|
module JSON
|
|
6
|
+
autoload :GenericObject, 'json/generic_object'
|
|
7
|
+
|
|
8
|
+
module ParserOptions # :nodoc:
|
|
9
|
+
class << self
|
|
10
|
+
def prepare(opts)
|
|
11
|
+
if opts[:object_class] || opts[:array_class]
|
|
12
|
+
opts = opts.dup
|
|
13
|
+
on_load = opts[:on_load]
|
|
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
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def object_class_proc(object_class, on_load)
|
|
30
|
+
->(obj) do
|
|
31
|
+
if Hash === obj
|
|
32
|
+
object = object_class.new
|
|
33
|
+
obj.each { |k, v| object[k] = v }
|
|
34
|
+
obj = object
|
|
35
|
+
end
|
|
36
|
+
on_load.nil? ? obj : on_load.call(obj)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def array_class_proc(array_class, on_load)
|
|
41
|
+
->(obj) do
|
|
42
|
+
if Array === obj
|
|
43
|
+
array = array_class.new
|
|
44
|
+
obj.each { |v| array << v }
|
|
45
|
+
obj = array
|
|
46
|
+
end
|
|
47
|
+
on_load.nil? ? obj : on_load.call(obj)
|
|
48
|
+
end
|
|
49
|
+
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
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
6
103
|
class << self
|
|
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
|
|
120
|
+
|
|
7
121
|
# :call-seq:
|
|
8
122
|
# JSON[object] -> new_array or new_string
|
|
9
123
|
#
|
|
@@ -15,17 +129,20 @@ module JSON
|
|
|
15
129
|
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
|
|
16
130
|
# ruby = [0, 1, nil]
|
|
17
131
|
# JSON[ruby] # => '[0,1,null]'
|
|
18
|
-
def [](object, opts =
|
|
19
|
-
if object.
|
|
20
|
-
JSON.parse(object
|
|
21
|
-
|
|
22
|
-
|
|
132
|
+
def [](object, opts = nil)
|
|
133
|
+
if object.is_a?(String)
|
|
134
|
+
return JSON.parse(object, opts)
|
|
135
|
+
elsif object.respond_to?(:to_str)
|
|
136
|
+
str = object.to_str
|
|
137
|
+
if str.is_a?(String)
|
|
138
|
+
return JSON.parse(str, opts)
|
|
139
|
+
end
|
|
23
140
|
end
|
|
141
|
+
|
|
142
|
+
JSON.generate(object, opts)
|
|
24
143
|
end
|
|
25
144
|
|
|
26
|
-
# Returns the JSON parser class that is used by JSON.
|
|
27
|
-
# JSON::Ext::Parser or JSON::Pure::Parser:
|
|
28
|
-
# JSON.parser # => JSON::Ext::Parser
|
|
145
|
+
# Returns the JSON parser class that is used by JSON.
|
|
29
146
|
attr_reader :parser
|
|
30
147
|
|
|
31
148
|
# Set the JSON parser class _parser_ to be used by JSON.
|
|
@@ -35,132 +152,162 @@ module JSON
|
|
|
35
152
|
const_set :Parser, parser
|
|
36
153
|
end
|
|
37
154
|
|
|
38
|
-
# Return the constant located at _path_. The format of _path_ has to be
|
|
39
|
-
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
|
|
40
|
-
# level (absolute namespace path?). If there doesn't exist a constant at
|
|
41
|
-
# the given path, an ArgumentError is raised.
|
|
42
|
-
def deep_const_get(path) # :nodoc:
|
|
43
|
-
path.to_s.split(/::/).inject(Object) do |p, c|
|
|
44
|
-
case
|
|
45
|
-
when c.empty? then p
|
|
46
|
-
when p.const_defined?(c, true) then p.const_get(c)
|
|
47
|
-
else
|
|
48
|
-
begin
|
|
49
|
-
p.const_missing(c)
|
|
50
|
-
rescue NameError => e
|
|
51
|
-
raise ArgumentError, "can't get const #{path}: #{e}"
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
155
|
# Set the module _generator_ to be used by JSON.
|
|
58
156
|
def generator=(generator) # :nodoc:
|
|
59
157
|
old, $VERBOSE = $VERBOSE, nil
|
|
158
|
+
|
|
159
|
+
# The default proc used when the +sort_keys+ generation option is +true+.
|
|
160
|
+
# It returns a new hash with the entries sorted by their keys.
|
|
161
|
+
sort_keys_proc = ->(hash) { hash.sort.to_h }
|
|
162
|
+
if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
|
|
163
|
+
sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
|
|
164
|
+
end
|
|
165
|
+
generator::State.default_sort_keys_proc = sort_keys_proc
|
|
166
|
+
|
|
60
167
|
@generator = generator
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
168
|
+
if generator.const_defined?(:GeneratorMethods)
|
|
169
|
+
generator_methods = generator::GeneratorMethods
|
|
170
|
+
for const in generator_methods.constants
|
|
171
|
+
klass = const_get(const)
|
|
172
|
+
modul = generator_methods.const_get(const)
|
|
173
|
+
klass.class_eval do
|
|
174
|
+
instance_methods(false).each do |m|
|
|
175
|
+
m.to_s == 'to_json' and remove_method m
|
|
176
|
+
end
|
|
177
|
+
include modul
|
|
68
178
|
end
|
|
69
|
-
include modul
|
|
70
179
|
end
|
|
71
180
|
end
|
|
72
181
|
self.state = generator::State
|
|
73
|
-
const_set :State,
|
|
74
|
-
const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
|
|
75
|
-
const_set :FAST_STATE_PROTOTYPE, create_fast_state
|
|
76
|
-
const_set :PRETTY_STATE_PROTOTYPE, create_pretty_state
|
|
182
|
+
const_set :State, state
|
|
77
183
|
ensure
|
|
78
184
|
$VERBOSE = old
|
|
79
185
|
end
|
|
80
186
|
|
|
81
|
-
|
|
82
|
-
State.new(
|
|
83
|
-
:indent => '',
|
|
84
|
-
:space => '',
|
|
85
|
-
:object_nl => "",
|
|
86
|
-
:array_nl => "",
|
|
87
|
-
:max_nesting => false
|
|
88
|
-
)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def create_pretty_state
|
|
92
|
-
State.new(
|
|
93
|
-
:indent => ' ',
|
|
94
|
-
:space => ' ',
|
|
95
|
-
:object_nl => "\n",
|
|
96
|
-
:array_nl => "\n"
|
|
97
|
-
)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Returns the JSON generator module that is used by JSON. This is
|
|
101
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
|
102
|
-
# JSON.generator # => JSON::Ext::Generator
|
|
187
|
+
# Returns the JSON generator module that is used by JSON.
|
|
103
188
|
attr_reader :generator
|
|
104
189
|
|
|
105
|
-
# Sets or Returns the JSON generator state class that is used by JSON.
|
|
106
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
|
107
|
-
# JSON.state # => JSON::Ext::Generator::State
|
|
190
|
+
# Sets or Returns the JSON generator state class that is used by JSON.
|
|
108
191
|
attr_accessor :state
|
|
109
|
-
end
|
|
110
192
|
|
|
111
|
-
|
|
112
|
-
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
# Called from the extension when a hash has both string and symbol keys
|
|
196
|
+
def on_mixed_keys_hash(hash, do_raise)
|
|
197
|
+
set = {}
|
|
198
|
+
hash.each_key do |key|
|
|
199
|
+
key_str = key.to_s
|
|
200
|
+
|
|
201
|
+
if set[key_str]
|
|
202
|
+
message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
|
|
203
|
+
if do_raise
|
|
204
|
+
raise GeneratorError, message
|
|
205
|
+
else
|
|
206
|
+
deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
|
|
207
|
+
end
|
|
208
|
+
else
|
|
209
|
+
set[key_str] = true
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def deprecated_singleton_attr_accessor(*attrs)
|
|
215
|
+
args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
|
|
216
|
+
attrs.each do |attr|
|
|
217
|
+
singleton_class.class_eval <<~RUBY
|
|
218
|
+
def #{attr}
|
|
219
|
+
warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
|
|
220
|
+
@#{attr}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def #{attr}=(val)
|
|
224
|
+
warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
|
|
225
|
+
@#{attr} = val
|
|
226
|
+
end
|
|
113
227
|
|
|
114
|
-
|
|
115
|
-
|
|
228
|
+
def _#{attr}
|
|
229
|
+
@#{attr}
|
|
230
|
+
end
|
|
231
|
+
RUBY
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
116
235
|
|
|
117
236
|
# Sets create identifier, which is used to decide if the _json_create_
|
|
118
237
|
# hook of a class should be called; initial value is +json_class+:
|
|
119
238
|
# JSON.create_id # => 'json_class'
|
|
120
239
|
def self.create_id=(new_value)
|
|
121
|
-
Thread.current[
|
|
240
|
+
Thread.current[:"JSON.create_id"] = new_value.dup.freeze
|
|
122
241
|
end
|
|
123
242
|
|
|
124
243
|
# Returns the current create identifier.
|
|
125
244
|
# See also JSON.create_id=.
|
|
126
245
|
def self.create_id
|
|
127
|
-
Thread.current[
|
|
246
|
+
Thread.current[:"JSON.create_id"] || 'json_class'
|
|
128
247
|
end
|
|
129
248
|
|
|
130
|
-
NaN =
|
|
249
|
+
NaN = Float::NAN
|
|
131
250
|
|
|
132
|
-
Infinity =
|
|
251
|
+
Infinity = Float::INFINITY
|
|
133
252
|
|
|
134
253
|
MinusInfinity = -Infinity
|
|
135
254
|
|
|
136
255
|
# The base exception for JSON errors.
|
|
137
|
-
class JSONError < StandardError
|
|
138
|
-
def self.wrap(exception)
|
|
139
|
-
obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
|
|
140
|
-
obj.set_backtrace exception.backtrace
|
|
141
|
-
obj
|
|
142
|
-
end
|
|
143
|
-
end
|
|
256
|
+
class JSONError < StandardError; end
|
|
144
257
|
|
|
145
258
|
# This exception is raised if a parser error occurs.
|
|
146
|
-
class ParserError < JSONError
|
|
259
|
+
class ParserError < JSONError
|
|
260
|
+
attr_reader :line, :column
|
|
261
|
+
end
|
|
147
262
|
|
|
148
263
|
# This exception is raised if the nesting of parsed data structures is too
|
|
149
264
|
# deep.
|
|
150
265
|
class NestingError < ParserError; end
|
|
151
266
|
|
|
152
|
-
# :stopdoc:
|
|
153
|
-
class CircularDatastructure < NestingError; end
|
|
154
|
-
# :startdoc:
|
|
155
|
-
|
|
156
267
|
# This exception is raised if a generator or unparser error occurs.
|
|
157
|
-
class GeneratorError < JSONError
|
|
158
|
-
|
|
159
|
-
|
|
268
|
+
class GeneratorError < JSONError
|
|
269
|
+
attr_reader :invalid_object
|
|
270
|
+
|
|
271
|
+
def initialize(message, invalid_object = nil)
|
|
272
|
+
super(message)
|
|
273
|
+
@invalid_object = invalid_object
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def detailed_message(...)
|
|
277
|
+
# Exception#detailed_message doesn't exist until Ruby 3.2
|
|
278
|
+
super_message = defined?(super) ? super : message
|
|
279
|
+
|
|
280
|
+
if @invalid_object.nil?
|
|
281
|
+
super_message
|
|
282
|
+
else
|
|
283
|
+
"#{super_message}\nInvalid object: #{@invalid_object.inspect}"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Fragment of JSON document that is to be included as is:
|
|
289
|
+
# fragment = JSON::Fragment.new("[1, 2, 3]")
|
|
290
|
+
# JSON.generate({ count: 3, items: fragments })
|
|
291
|
+
#
|
|
292
|
+
# This allows to easily assemble multiple JSON fragments that have
|
|
293
|
+
# been persisted somewhere without having to parse them nor resorting
|
|
294
|
+
# to string interpolation.
|
|
295
|
+
#
|
|
296
|
+
# Note: no validation is performed on the provided string. It is the
|
|
297
|
+
# responsibility of the caller to ensure the string contains valid JSON.
|
|
298
|
+
Fragment = Struct.new(:json) do
|
|
299
|
+
def initialize(json)
|
|
300
|
+
unless string = String.try_convert(json)
|
|
301
|
+
raise TypeError, " no implicit conversion of #{json.class} into String"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
super(string)
|
|
305
|
+
end
|
|
160
306
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
307
|
+
def to_json(state = nil, *)
|
|
308
|
+
json
|
|
309
|
+
end
|
|
310
|
+
end
|
|
164
311
|
|
|
165
312
|
module_function
|
|
166
313
|
|
|
@@ -192,17 +339,17 @@ module JSON
|
|
|
192
339
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
|
193
340
|
#
|
|
194
341
|
# Parses nested JSON objects:
|
|
195
|
-
# source =
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
#
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
342
|
+
# source = <<~JSON
|
|
343
|
+
# {
|
|
344
|
+
# "name": "Dave",
|
|
345
|
+
# "age" :40,
|
|
346
|
+
# "hats": [
|
|
347
|
+
# "Cattleman's",
|
|
348
|
+
# "Panama",
|
|
349
|
+
# "Tophat"
|
|
350
|
+
# ]
|
|
351
|
+
# }
|
|
352
|
+
# JSON
|
|
206
353
|
# ruby = JSON.parse(source)
|
|
207
354
|
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
|
208
355
|
#
|
|
@@ -212,10 +359,17 @@ module JSON
|
|
|
212
359
|
# # Raises JSON::ParserError (783: unexpected token at ''):
|
|
213
360
|
# JSON.parse('')
|
|
214
361
|
#
|
|
215
|
-
def parse(source, opts =
|
|
216
|
-
|
|
362
|
+
def parse(source, opts = nil)
|
|
363
|
+
opts = ParserOptions.prepare(opts) unless opts.nil?
|
|
364
|
+
Parser.parse(source, opts)
|
|
217
365
|
end
|
|
218
366
|
|
|
367
|
+
PARSE_L_OPTIONS = {
|
|
368
|
+
max_nesting: false,
|
|
369
|
+
allow_nan: true,
|
|
370
|
+
}.freeze
|
|
371
|
+
private_constant :PARSE_L_OPTIONS
|
|
372
|
+
|
|
219
373
|
# :call-seq:
|
|
220
374
|
# JSON.parse!(source, opts) -> object
|
|
221
375
|
#
|
|
@@ -227,12 +381,12 @@ module JSON
|
|
|
227
381
|
# - Option +max_nesting+, if not provided, defaults to +false+,
|
|
228
382
|
# which disables checking for nesting depth.
|
|
229
383
|
# - Option +allow_nan+, if not provided, defaults to +true+.
|
|
230
|
-
def parse!(source, opts =
|
|
231
|
-
opts
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
384
|
+
def parse!(source, opts = nil)
|
|
385
|
+
if opts.nil?
|
|
386
|
+
parse(source, PARSE_L_OPTIONS)
|
|
387
|
+
else
|
|
388
|
+
parse(source, PARSE_L_OPTIONS.merge(opts))
|
|
389
|
+
end
|
|
236
390
|
end
|
|
237
391
|
|
|
238
392
|
# :call-seq:
|
|
@@ -242,8 +396,8 @@ module JSON
|
|
|
242
396
|
# parse(File.read(path), opts)
|
|
243
397
|
#
|
|
244
398
|
# See method #parse.
|
|
245
|
-
def load_file(filespec, opts =
|
|
246
|
-
parse(File.read(filespec), opts)
|
|
399
|
+
def load_file(filespec, opts = nil)
|
|
400
|
+
parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
|
247
401
|
end
|
|
248
402
|
|
|
249
403
|
# :call-seq:
|
|
@@ -253,8 +407,8 @@ module JSON
|
|
|
253
407
|
# JSON.parse!(File.read(path, opts))
|
|
254
408
|
#
|
|
255
409
|
# See method #parse!
|
|
256
|
-
def load_file!(filespec, opts =
|
|
257
|
-
parse!(File.read(filespec), opts)
|
|
410
|
+
def load_file!(filespec, opts = nil)
|
|
411
|
+
parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
|
|
258
412
|
end
|
|
259
413
|
|
|
260
414
|
# :call-seq:
|
|
@@ -262,7 +416,7 @@ module JSON
|
|
|
262
416
|
#
|
|
263
417
|
# Returns a \String containing the generated \JSON data.
|
|
264
418
|
#
|
|
265
|
-
# See also JSON.
|
|
419
|
+
# See also JSON.pretty_generate.
|
|
266
420
|
#
|
|
267
421
|
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
|
268
422
|
#
|
|
@@ -295,30 +449,12 @@ module JSON
|
|
|
295
449
|
#
|
|
296
450
|
def generate(obj, opts = nil)
|
|
297
451
|
if State === opts
|
|
298
|
-
|
|
452
|
+
opts.generate(obj)
|
|
299
453
|
else
|
|
300
|
-
|
|
454
|
+
State.generate(obj, opts, nil)
|
|
301
455
|
end
|
|
302
|
-
if opts
|
|
303
|
-
if opts.respond_to? :to_hash
|
|
304
|
-
opts = opts.to_hash
|
|
305
|
-
elsif opts.respond_to? :to_h
|
|
306
|
-
opts = opts.to_h
|
|
307
|
-
else
|
|
308
|
-
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
309
|
-
end
|
|
310
|
-
state = state.configure(opts)
|
|
311
|
-
end
|
|
312
|
-
state.generate(obj)
|
|
313
456
|
end
|
|
314
457
|
|
|
315
|
-
# :stopdoc:
|
|
316
|
-
# I want to deprecate these later, so I'll first be silent about them, and
|
|
317
|
-
# later delete them.
|
|
318
|
-
alias unparse generate
|
|
319
|
-
module_function :unparse
|
|
320
|
-
# :startdoc:
|
|
321
|
-
|
|
322
458
|
# :call-seq:
|
|
323
459
|
# JSON.fast_generate(obj, opts) -> new_string
|
|
324
460
|
#
|
|
@@ -333,29 +469,21 @@ module JSON
|
|
|
333
469
|
# # Raises SystemStackError (stack level too deep):
|
|
334
470
|
# JSON.fast_generate(a)
|
|
335
471
|
def fast_generate(obj, opts = nil)
|
|
336
|
-
if
|
|
337
|
-
|
|
472
|
+
if RUBY_VERSION >= "3.0"
|
|
473
|
+
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
338
474
|
else
|
|
339
|
-
|
|
475
|
+
warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
340
476
|
end
|
|
341
|
-
|
|
342
|
-
if opts.respond_to? :to_hash
|
|
343
|
-
opts = opts.to_hash
|
|
344
|
-
elsif opts.respond_to? :to_h
|
|
345
|
-
opts = opts.to_h
|
|
346
|
-
else
|
|
347
|
-
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
348
|
-
end
|
|
349
|
-
state.configure(opts)
|
|
350
|
-
end
|
|
351
|
-
state.generate(obj)
|
|
477
|
+
generate(obj, opts)
|
|
352
478
|
end
|
|
353
479
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
480
|
+
PRETTY_GENERATE_OPTIONS = {
|
|
481
|
+
indent: ' ',
|
|
482
|
+
space: ' ',
|
|
483
|
+
object_nl: "\n",
|
|
484
|
+
array_nl: "\n",
|
|
485
|
+
}.freeze
|
|
486
|
+
private_constant :PRETTY_GENERATE_OPTIONS
|
|
359
487
|
|
|
360
488
|
# :call-seq:
|
|
361
489
|
# JSON.pretty_generate(obj, opts = nil) -> new_string
|
|
@@ -388,49 +516,60 @@ module JSON
|
|
|
388
516
|
# }
|
|
389
517
|
#
|
|
390
518
|
def pretty_generate(obj, opts = nil)
|
|
391
|
-
if State === opts
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
end
|
|
519
|
+
return opts.generate(obj) if State === opts
|
|
520
|
+
|
|
521
|
+
options = PRETTY_GENERATE_OPTIONS
|
|
522
|
+
|
|
396
523
|
if opts
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
524
|
+
unless opts.is_a?(Hash)
|
|
525
|
+
if opts.respond_to? :to_hash
|
|
526
|
+
opts = opts.to_hash
|
|
527
|
+
elsif opts.respond_to? :to_h
|
|
528
|
+
opts = opts.to_h
|
|
529
|
+
else
|
|
530
|
+
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
531
|
+
end
|
|
403
532
|
end
|
|
404
|
-
|
|
533
|
+
options = options.merge(opts)
|
|
405
534
|
end
|
|
406
|
-
|
|
535
|
+
|
|
536
|
+
State.generate(obj, options, nil)
|
|
407
537
|
end
|
|
408
538
|
|
|
409
|
-
#
|
|
410
|
-
#
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
539
|
+
# Sets or returns default options for the JSON.unsafe_load method.
|
|
540
|
+
# Initially:
|
|
541
|
+
# opts = JSON.load_default_options
|
|
542
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
543
|
+
deprecated_singleton_attr_accessor :unsafe_load_default_options
|
|
414
544
|
|
|
415
|
-
|
|
416
|
-
# Sets or returns default options for the JSON.load method.
|
|
417
|
-
# Initially:
|
|
418
|
-
# opts = JSON.load_default_options
|
|
419
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
420
|
-
attr_accessor :load_default_options
|
|
421
|
-
end
|
|
422
|
-
self.load_default_options = {
|
|
545
|
+
@unsafe_load_default_options = {
|
|
423
546
|
:max_nesting => false,
|
|
424
547
|
:allow_nan => true,
|
|
425
|
-
:allow_blank
|
|
548
|
+
:allow_blank => true,
|
|
426
549
|
:create_additions => true,
|
|
427
550
|
}
|
|
428
551
|
|
|
552
|
+
# Sets or returns default options for the JSON.load method.
|
|
553
|
+
# Initially:
|
|
554
|
+
# opts = JSON.load_default_options
|
|
555
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
|
556
|
+
deprecated_singleton_attr_accessor :load_default_options
|
|
557
|
+
|
|
558
|
+
@load_default_options = {
|
|
559
|
+
:allow_nan => true,
|
|
560
|
+
:allow_blank => true,
|
|
561
|
+
:create_additions => nil,
|
|
562
|
+
}
|
|
429
563
|
# :call-seq:
|
|
430
|
-
# JSON.
|
|
564
|
+
# JSON.unsafe_load(source, options = {}) -> object
|
|
565
|
+
# JSON.unsafe_load(source, proc = nil, options = {}) -> object
|
|
431
566
|
#
|
|
432
567
|
# Returns the Ruby objects created by parsing the given +source+.
|
|
433
568
|
#
|
|
569
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
|
570
|
+
# like from your own database server or clients under your control, it could
|
|
571
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
572
|
+
#
|
|
434
573
|
# - Argument +source+ must be, or be convertible to, a \String:
|
|
435
574
|
# - If +source+ responds to instance method +to_str+,
|
|
436
575
|
# <tt>source.to_str</tt> becomes the source.
|
|
@@ -445,12 +584,9 @@ module JSON
|
|
|
445
584
|
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
|
446
585
|
# It will be called recursively with each result (depth-first order).
|
|
447
586
|
# See details below.
|
|
448
|
-
# BEWARE: This method is meant to serialise data from trusted user input,
|
|
449
|
-
# like from your own database server or clients under your control, it could
|
|
450
|
-
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
451
587
|
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
452
588
|
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
453
|
-
# The default options can be changed via method JSON.
|
|
589
|
+
# The default options can be changed via method JSON.unsafe_load_default_options=.
|
|
454
590
|
#
|
|
455
591
|
# ---
|
|
456
592
|
#
|
|
@@ -458,17 +594,188 @@ module JSON
|
|
|
458
594
|
# <tt>parse(source, opts)</tt>; see #parse.
|
|
459
595
|
#
|
|
460
596
|
# Source for following examples:
|
|
461
|
-
# source =
|
|
597
|
+
# source = <<~JSON
|
|
598
|
+
# {
|
|
599
|
+
# "name": "Dave",
|
|
600
|
+
# "age" :40,
|
|
601
|
+
# "hats": [
|
|
602
|
+
# "Cattleman's",
|
|
603
|
+
# "Panama",
|
|
604
|
+
# "Tophat"
|
|
605
|
+
# ]
|
|
606
|
+
# }
|
|
607
|
+
# JSON
|
|
608
|
+
#
|
|
609
|
+
# Load a \String:
|
|
610
|
+
# ruby = JSON.unsafe_load(source)
|
|
611
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
|
612
|
+
#
|
|
613
|
+
# Load an \IO object:
|
|
614
|
+
# require 'stringio'
|
|
615
|
+
# object = JSON.unsafe_load(StringIO.new(source))
|
|
616
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
|
617
|
+
#
|
|
618
|
+
# Load a \File object:
|
|
619
|
+
# path = 't.json'
|
|
620
|
+
# File.write(path, source)
|
|
621
|
+
# File.open(path) do |file|
|
|
622
|
+
# JSON.unsafe_load(file)
|
|
623
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
|
624
|
+
#
|
|
625
|
+
# ---
|
|
626
|
+
#
|
|
627
|
+
# When +proc+ is given:
|
|
628
|
+
# - Modifies +source+ as above.
|
|
629
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
|
630
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
|
631
|
+
# - Returns the final result.
|
|
632
|
+
#
|
|
633
|
+
# Example:
|
|
634
|
+
# require 'json'
|
|
635
|
+
#
|
|
636
|
+
# # Some classes for the example.
|
|
637
|
+
# class Base
|
|
638
|
+
# def initialize(attributes)
|
|
639
|
+
# @attributes = attributes
|
|
640
|
+
# end
|
|
641
|
+
# end
|
|
642
|
+
# class User < Base; end
|
|
643
|
+
# class Account < Base; end
|
|
644
|
+
# class Admin < Base; end
|
|
645
|
+
# # The JSON source.
|
|
646
|
+
# json = <<-EOF
|
|
462
647
|
# {
|
|
463
|
-
#
|
|
464
|
-
#
|
|
465
|
-
#
|
|
466
|
-
#
|
|
467
|
-
#
|
|
468
|
-
#
|
|
469
|
-
#
|
|
648
|
+
# "users": [
|
|
649
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
|
650
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
|
651
|
+
# ],
|
|
652
|
+
# "accounts": [
|
|
653
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
|
654
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
|
655
|
+
# ],
|
|
656
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
|
470
657
|
# }
|
|
471
|
-
#
|
|
658
|
+
# EOF
|
|
659
|
+
# # Deserializer method.
|
|
660
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
|
661
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
|
662
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
|
663
|
+
# end
|
|
664
|
+
# # Call to JSON.unsafe_load
|
|
665
|
+
# ruby = JSON.unsafe_load(json, proc {|obj|
|
|
666
|
+
# case obj
|
|
667
|
+
# when Hash
|
|
668
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
|
669
|
+
# when Array
|
|
670
|
+
# obj.map! {|v| deserialize_obj v }
|
|
671
|
+
# end
|
|
672
|
+
# obj
|
|
673
|
+
# })
|
|
674
|
+
# pp ruby
|
|
675
|
+
# Output:
|
|
676
|
+
# {"users"=>
|
|
677
|
+
# [#<User:0x00000000064c4c98
|
|
678
|
+
# @attributes=
|
|
679
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
|
680
|
+
# #<User:0x00000000064c4bd0
|
|
681
|
+
# @attributes=
|
|
682
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
|
683
|
+
# "accounts"=>
|
|
684
|
+
# [{"account"=>
|
|
685
|
+
# #<Account:0x00000000064c4928
|
|
686
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
|
687
|
+
# {"account"=>
|
|
688
|
+
# #<Account:0x00000000064c4680
|
|
689
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
|
690
|
+
# "admins"=>
|
|
691
|
+
# #<Admin:0x00000000064c41f8
|
|
692
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
693
|
+
#
|
|
694
|
+
def unsafe_load(source, proc = nil, options = nil)
|
|
695
|
+
opts = if options.nil?
|
|
696
|
+
if proc && proc.is_a?(Hash)
|
|
697
|
+
options, proc = proc, nil
|
|
698
|
+
options
|
|
699
|
+
else
|
|
700
|
+
_unsafe_load_default_options
|
|
701
|
+
end
|
|
702
|
+
else
|
|
703
|
+
_unsafe_load_default_options.merge(options)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
unless source.is_a?(String)
|
|
707
|
+
if source.respond_to? :to_str
|
|
708
|
+
source = source.to_str
|
|
709
|
+
elsif source.respond_to? :to_io
|
|
710
|
+
source = source.to_io.read
|
|
711
|
+
elsif source.respond_to?(:read)
|
|
712
|
+
source = source.read
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
717
|
+
source = 'null'
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
if proc
|
|
721
|
+
opts = opts.dup
|
|
722
|
+
opts[:on_load] = proc.to_proc
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
parse(source, opts)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
# :call-seq:
|
|
729
|
+
# JSON.load(source, options = {}) -> object
|
|
730
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
|
731
|
+
#
|
|
732
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
|
733
|
+
#
|
|
734
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
|
735
|
+
# like from your own database server or clients under your control, it could
|
|
736
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
|
737
|
+
# If you must use it, use JSON.unsafe_load instead to make it clear.
|
|
738
|
+
#
|
|
739
|
+
# Since JSON version 2.8.0, `load` emits a deprecation warning when a
|
|
740
|
+
# non native type is deserialized, without `create_additions` being explicitly
|
|
741
|
+
# enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
|
|
742
|
+
# by default.
|
|
743
|
+
#
|
|
744
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
|
745
|
+
# - If +source+ responds to instance method +to_str+,
|
|
746
|
+
# <tt>source.to_str</tt> becomes the source.
|
|
747
|
+
# - If +source+ responds to instance method +to_io+,
|
|
748
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
|
749
|
+
# - If +source+ responds to instance method +read+,
|
|
750
|
+
# <tt>source.read</tt> becomes the source.
|
|
751
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
|
752
|
+
# - Option +allow_blank+ specifies a truthy value.
|
|
753
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
|
754
|
+
# - Otherwise, +source+ remains the source.
|
|
755
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
|
756
|
+
# It will be called recursively with each result (depth-first order).
|
|
757
|
+
# See details below.
|
|
758
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
|
759
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
|
760
|
+
# The default options can be changed via method JSON.load_default_options=.
|
|
761
|
+
#
|
|
762
|
+
# ---
|
|
763
|
+
#
|
|
764
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
|
765
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
|
766
|
+
#
|
|
767
|
+
# Source for following examples:
|
|
768
|
+
# source = <<~JSON
|
|
769
|
+
# {
|
|
770
|
+
# "name": "Dave",
|
|
771
|
+
# "age" :40,
|
|
772
|
+
# "hats": [
|
|
773
|
+
# "Cattleman's",
|
|
774
|
+
# "Panama",
|
|
775
|
+
# "Tophat"
|
|
776
|
+
# ]
|
|
777
|
+
# }
|
|
778
|
+
# JSON
|
|
472
779
|
#
|
|
473
780
|
# Load a \String:
|
|
474
781
|
# ruby = JSON.load(source)
|
|
@@ -533,6 +840,7 @@ module JSON
|
|
|
533
840
|
# when Array
|
|
534
841
|
# obj.map! {|v| deserialize_obj v }
|
|
535
842
|
# end
|
|
843
|
+
# obj
|
|
536
844
|
# })
|
|
537
845
|
# pp ruby
|
|
538
846
|
# Output:
|
|
@@ -554,51 +862,53 @@ module JSON
|
|
|
554
862
|
# #<Admin:0x00000000064c41f8
|
|
555
863
|
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
|
556
864
|
#
|
|
557
|
-
def load(source, proc = nil, options =
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
elsif source.respond_to? :to_io
|
|
562
|
-
source = source.to_io.read
|
|
563
|
-
elsif source.respond_to?(:read)
|
|
564
|
-
source = source.read
|
|
865
|
+
def load(source, proc = nil, options = nil)
|
|
866
|
+
if proc && options.nil? && proc.is_a?(Hash)
|
|
867
|
+
options = proc
|
|
868
|
+
proc = nil
|
|
565
869
|
end
|
|
566
|
-
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
567
|
-
source = 'null'
|
|
568
|
-
end
|
|
569
|
-
result = parse(source, opts)
|
|
570
|
-
recurse_proc(result, &proc) if proc
|
|
571
|
-
result
|
|
572
|
-
end
|
|
573
870
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
|
|
582
|
-
proc.call result
|
|
871
|
+
opts = if options.nil?
|
|
872
|
+
if proc && proc.is_a?(Hash)
|
|
873
|
+
options, proc = proc, nil
|
|
874
|
+
options
|
|
875
|
+
else
|
|
876
|
+
_load_default_options
|
|
877
|
+
end
|
|
583
878
|
else
|
|
584
|
-
|
|
879
|
+
_load_default_options.merge(options)
|
|
585
880
|
end
|
|
586
|
-
end
|
|
587
881
|
|
|
588
|
-
|
|
589
|
-
|
|
882
|
+
unless source.is_a?(String)
|
|
883
|
+
if source.respond_to? :to_str
|
|
884
|
+
source = source.to_str
|
|
885
|
+
elsif source.respond_to? :to_io
|
|
886
|
+
source = source.to_io.read
|
|
887
|
+
elsif source.respond_to?(:read)
|
|
888
|
+
source = source.read
|
|
889
|
+
end
|
|
890
|
+
end
|
|
590
891
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
892
|
+
if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
|
|
893
|
+
source = 'null'
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
if proc
|
|
897
|
+
opts = opts.dup
|
|
898
|
+
opts[:on_load] = proc.to_proc
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
parse(source, opts)
|
|
597
902
|
end
|
|
598
|
-
|
|
903
|
+
|
|
904
|
+
# Sets or returns the default options for the JSON.dump method.
|
|
905
|
+
# Initially:
|
|
906
|
+
# opts = JSON.dump_default_options
|
|
907
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
|
908
|
+
deprecated_singleton_attr_accessor :dump_default_options
|
|
909
|
+
@dump_default_options = {
|
|
599
910
|
:max_nesting => false,
|
|
600
911
|
:allow_nan => true,
|
|
601
|
-
:escape_slash => false,
|
|
602
912
|
}
|
|
603
913
|
|
|
604
914
|
# :call-seq:
|
|
@@ -628,30 +938,198 @@ module JSON
|
|
|
628
938
|
# puts File.read(path)
|
|
629
939
|
# Output:
|
|
630
940
|
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
|
631
|
-
def dump(obj, anIO = nil, limit = nil)
|
|
632
|
-
if
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
941
|
+
def dump(obj, anIO = nil, limit = nil, kwargs = nil)
|
|
942
|
+
if kwargs.nil?
|
|
943
|
+
if limit.nil?
|
|
944
|
+
if anIO.is_a?(Hash)
|
|
945
|
+
kwargs = anIO
|
|
946
|
+
anIO = nil
|
|
947
|
+
end
|
|
948
|
+
elsif limit.is_a?(Hash)
|
|
949
|
+
kwargs = limit
|
|
950
|
+
limit = nil
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
unless anIO.nil?
|
|
955
|
+
if anIO.respond_to?(:to_io)
|
|
956
|
+
anIO = anIO.to_io
|
|
957
|
+
elsif limit.nil? && !anIO.respond_to?(:write)
|
|
958
|
+
anIO, limit = nil, anIO
|
|
637
959
|
end
|
|
638
960
|
end
|
|
639
|
-
|
|
961
|
+
|
|
962
|
+
opts = JSON._dump_default_options
|
|
640
963
|
opts = opts.merge(:max_nesting => limit) if limit
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
anIO
|
|
964
|
+
opts = opts.merge(kwargs) if kwargs
|
|
965
|
+
|
|
966
|
+
begin
|
|
967
|
+
State.generate(obj, opts, anIO)
|
|
968
|
+
rescue JSON::NestingError
|
|
969
|
+
raise ArgumentError, "exceed depth limit"
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
# :stopdoc:
|
|
974
|
+
# All these were meant to be deprecated circa 2009, but were just set as undocumented
|
|
975
|
+
# so usage still exist in the wild.
|
|
976
|
+
def unparse(...)
|
|
977
|
+
if RUBY_VERSION >= "3.0"
|
|
978
|
+
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
645
979
|
else
|
|
646
|
-
|
|
980
|
+
warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
647
981
|
end
|
|
648
|
-
|
|
649
|
-
raise ArgumentError, "exceed depth limit"
|
|
982
|
+
generate(...)
|
|
650
983
|
end
|
|
984
|
+
module_function :unparse
|
|
651
985
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
986
|
+
def fast_unparse(...)
|
|
987
|
+
if RUBY_VERSION >= "3.0"
|
|
988
|
+
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
|
|
989
|
+
else
|
|
990
|
+
warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
|
|
991
|
+
end
|
|
992
|
+
generate(...)
|
|
993
|
+
end
|
|
994
|
+
module_function :fast_unparse
|
|
995
|
+
|
|
996
|
+
def pretty_unparse(...)
|
|
997
|
+
if RUBY_VERSION >= "3.0"
|
|
998
|
+
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
|
|
999
|
+
else
|
|
1000
|
+
warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
1001
|
+
end
|
|
1002
|
+
pretty_generate(...)
|
|
1003
|
+
end
|
|
1004
|
+
module_function :fast_unparse
|
|
1005
|
+
|
|
1006
|
+
def restore(...)
|
|
1007
|
+
if RUBY_VERSION >= "3.0"
|
|
1008
|
+
warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
|
|
1009
|
+
else
|
|
1010
|
+
warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
|
|
1011
|
+
end
|
|
1012
|
+
load(...)
|
|
1013
|
+
end
|
|
1014
|
+
module_function :restore
|
|
1015
|
+
|
|
1016
|
+
class << self
|
|
1017
|
+
private
|
|
1018
|
+
|
|
1019
|
+
def const_missing(const_name)
|
|
1020
|
+
case const_name
|
|
1021
|
+
when :PRETTY_STATE_PROTOTYPE
|
|
1022
|
+
if RUBY_VERSION >= "3.0"
|
|
1023
|
+
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
|
|
1024
|
+
else
|
|
1025
|
+
warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
|
|
1026
|
+
end
|
|
1027
|
+
state.new(PRETTY_GENERATE_OPTIONS)
|
|
1028
|
+
else
|
|
1029
|
+
super
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
end
|
|
1033
|
+
# :startdoc:
|
|
1034
|
+
|
|
1035
|
+
# JSON::Coder holds a parser and generator configuration.
|
|
1036
|
+
#
|
|
1037
|
+
# module MyApp
|
|
1038
|
+
# JSONC_CODER = JSON::Coder.new(
|
|
1039
|
+
# allow_trailing_comma: true
|
|
1040
|
+
# )
|
|
1041
|
+
# end
|
|
1042
|
+
#
|
|
1043
|
+
# MyApp::JSONC_CODER.load(document)
|
|
1044
|
+
#
|
|
1045
|
+
class Coder
|
|
1046
|
+
# :call-seq:
|
|
1047
|
+
# JSON.new(options = nil, &block)
|
|
1048
|
+
#
|
|
1049
|
+
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
|
1050
|
+
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
1051
|
+
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
1052
|
+
#
|
|
1053
|
+
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1054
|
+
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
1055
|
+
# \JSON counterpart:
|
|
1056
|
+
#
|
|
1057
|
+
# module MyApp
|
|
1058
|
+
# API_JSON_CODER = JSON::Coder.new do |object|
|
|
1059
|
+
# case object
|
|
1060
|
+
# when Time
|
|
1061
|
+
# object.iso8601(3)
|
|
1062
|
+
# else
|
|
1063
|
+
# object # Unknown type, will raise
|
|
1064
|
+
# end
|
|
1065
|
+
# end
|
|
1066
|
+
# end
|
|
1067
|
+
#
|
|
1068
|
+
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
1069
|
+
#
|
|
1070
|
+
def initialize(options = nil, &as_json)
|
|
1071
|
+
if options.nil?
|
|
1072
|
+
options = { strict: true }
|
|
1073
|
+
else
|
|
1074
|
+
options = options.dup
|
|
1075
|
+
options[:strict] = true
|
|
1076
|
+
end
|
|
1077
|
+
options[:as_json] = as_json if as_json
|
|
1078
|
+
|
|
1079
|
+
@state = State.new(options).freeze
|
|
1080
|
+
@parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
# call-seq:
|
|
1084
|
+
# dump(object) -> String
|
|
1085
|
+
# dump(object, io) -> io
|
|
1086
|
+
#
|
|
1087
|
+
# Serialize the given object into a \JSON document.
|
|
1088
|
+
def dump(object, io = nil)
|
|
1089
|
+
@state.generate(object, io)
|
|
1090
|
+
end
|
|
1091
|
+
alias_method :generate, :dump
|
|
1092
|
+
|
|
1093
|
+
# call-seq:
|
|
1094
|
+
# load(string) -> Object
|
|
1095
|
+
#
|
|
1096
|
+
# Parse the given \JSON document and return an equivalent Ruby object.
|
|
1097
|
+
def load(source)
|
|
1098
|
+
@parser_config.parse(source)
|
|
1099
|
+
end
|
|
1100
|
+
alias_method :parse, :load
|
|
1101
|
+
|
|
1102
|
+
# call-seq:
|
|
1103
|
+
# load(path) -> Object
|
|
1104
|
+
#
|
|
1105
|
+
# Parse the given \JSON document and return an equivalent Ruby object.
|
|
1106
|
+
def load_file(path)
|
|
1107
|
+
load(File.read(path, encoding: Encoding::UTF_8))
|
|
1108
|
+
end
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
module GeneratorMethods
|
|
1112
|
+
# call-seq: to_json(*)
|
|
1113
|
+
#
|
|
1114
|
+
# Converts this object into a JSON string.
|
|
1115
|
+
# If this object doesn't directly maps to a JSON native type,
|
|
1116
|
+
# first convert it to a string (calling #to_s), then converts
|
|
1117
|
+
# it to a JSON string, and returns the result.
|
|
1118
|
+
# This is a fallback, if no special method #to_json was defined for some object.
|
|
1119
|
+
def to_json(state = nil, *)
|
|
1120
|
+
obj = case self
|
|
1121
|
+
when nil, false, true, Integer, Float, Array, Hash
|
|
1122
|
+
self
|
|
1123
|
+
else
|
|
1124
|
+
"#{self}"
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
if state.nil?
|
|
1128
|
+
JSON::State._generate_no_fallback(obj, nil, nil)
|
|
1129
|
+
else
|
|
1130
|
+
JSON::State.from_state(state)._generate_no_fallback(obj)
|
|
1131
|
+
end
|
|
1132
|
+
end
|
|
655
1133
|
end
|
|
656
1134
|
end
|
|
657
1135
|
|
|
@@ -661,8 +1139,14 @@ module ::Kernel
|
|
|
661
1139
|
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
|
|
662
1140
|
# one line.
|
|
663
1141
|
def j(*objs)
|
|
1142
|
+
if RUBY_VERSION >= "3.0"
|
|
1143
|
+
warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
|
|
1144
|
+
else
|
|
1145
|
+
warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1
|
|
1146
|
+
end
|
|
1147
|
+
|
|
664
1148
|
objs.each do |obj|
|
|
665
|
-
puts JSON
|
|
1149
|
+
puts JSON.generate(obj, :allow_nan => true, :max_nesting => false)
|
|
666
1150
|
end
|
|
667
1151
|
nil
|
|
668
1152
|
end
|
|
@@ -670,8 +1154,14 @@ module ::Kernel
|
|
|
670
1154
|
# Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
|
|
671
1155
|
# indentation and over many lines.
|
|
672
1156
|
def jj(*objs)
|
|
1157
|
+
if RUBY_VERSION >= "3.0"
|
|
1158
|
+
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
|
|
1159
|
+
else
|
|
1160
|
+
warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
|
|
1161
|
+
end
|
|
1162
|
+
|
|
673
1163
|
objs.each do |obj|
|
|
674
|
-
puts JSON
|
|
1164
|
+
puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
|
|
675
1165
|
end
|
|
676
1166
|
nil
|
|
677
1167
|
end
|
|
@@ -682,22 +1172,11 @@ module ::Kernel
|
|
|
682
1172
|
#
|
|
683
1173
|
# The _opts_ argument is passed through to generate/parse respectively. See
|
|
684
1174
|
# generate and parse for their documentation.
|
|
685
|
-
def JSON(object,
|
|
686
|
-
|
|
687
|
-
JSON.parse(object.to_str, args.first)
|
|
688
|
-
else
|
|
689
|
-
JSON.generate(object, args.first)
|
|
690
|
-
end
|
|
1175
|
+
def JSON(object, opts = nil)
|
|
1176
|
+
JSON[object, opts]
|
|
691
1177
|
end
|
|
692
1178
|
end
|
|
693
1179
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
# Returns true if this class can be used to create an instance
|
|
697
|
-
# from a serialised JSON string. The class has to implement a class
|
|
698
|
-
# method _json_create_ that expects a hash as first parameter. The hash
|
|
699
|
-
# should include the required data.
|
|
700
|
-
def json_creatable?
|
|
701
|
-
respond_to?(:json_create)
|
|
702
|
-
end
|
|
1180
|
+
class Object
|
|
1181
|
+
include JSON::GeneratorMethods
|
|
703
1182
|
end
|