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