json 2.3.1 → 2.5.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/CHANGES.md +22 -0
- data/LICENSE +56 -0
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +60 -11
- data/ext/json/ext/generator/generator.h +5 -2
- data/ext/json/ext/parser/extconf.rb +25 -0
- data/ext/json/ext/parser/parser.c +110 -68
- data/ext/json/ext/parser/parser.h +1 -0
- data/ext/json/ext/parser/parser.rl +67 -25
- data/ext/json/extconf.rb +1 -0
- data/json.gemspec +11 -77
- data/lib/json.rb +171 -0
- data/lib/json/add/complex.rb +0 -1
- data/lib/json/add/rational.rb +0 -1
- data/lib/json/common.rb +240 -228
- data/lib/json/pure/generator.rb +28 -8
- data/lib/json/pure/parser.rb +20 -2
- data/lib/json/version.rb +1 -1
- data/tests/fixtures/fail29.json +1 -0
- data/tests/fixtures/fail30.json +1 -0
- data/tests/fixtures/fail31.json +1 -0
- data/tests/fixtures/fail32.json +1 -0
- data/tests/json_addition_test.rb +0 -4
- data/tests/json_common_interface_test.rb +43 -0
- data/tests/json_fixtures_test.rb +3 -0
- data/tests/json_generator_test.rb +16 -38
- data/tests/json_parser_test.rb +25 -0
- data/tests/lib/core_assertions.rb +763 -0
- data/tests/lib/envutil.rb +365 -0
- data/tests/lib/find_executable.rb +22 -0
- data/tests/lib/helper.rb +4 -0
- data/tests/ractor_test.rb +30 -0
- data/tests/test_helper.rb +3 -3
- metadata +16 -37
- data/.gitignore +0 -18
- data/.travis.yml +0 -26
- data/README-json-jruby.md +0 -33
- data/Rakefile +0 -334
- data/diagrams/.keep +0 -0
- data/install.rb +0 -23
- data/java/src/json/ext/ByteListTranscoder.java +0 -166
- data/java/src/json/ext/Generator.java +0 -466
- data/java/src/json/ext/GeneratorMethods.java +0 -231
- data/java/src/json/ext/GeneratorService.java +0 -42
- data/java/src/json/ext/GeneratorState.java +0 -490
- data/java/src/json/ext/OptionsReader.java +0 -113
- data/java/src/json/ext/Parser.java +0 -2362
- data/java/src/json/ext/Parser.rl +0 -893
- data/java/src/json/ext/ParserService.java +0 -34
- data/java/src/json/ext/RuntimeInfo.java +0 -116
- data/java/src/json/ext/StringDecoder.java +0 -166
- data/java/src/json/ext/StringEncoder.java +0 -111
- data/java/src/json/ext/Utils.java +0 -88
- data/json-java.gemspec +0 -37
- data/json_pure.gemspec +0 -33
- data/references/rfc7159.txt +0 -899
- data/tools/diff.sh +0 -18
- data/tools/fuzz.rb +0 -131
- data/tools/server.rb +0 -62
data/lib/json/add/complex.rb
CHANGED
data/lib/json/add/rational.rb
CHANGED
data/lib/json/common.rb
CHANGED
@@ -4,13 +4,15 @@ require 'json/generic_object'
|
|
4
4
|
|
5
5
|
module JSON
|
6
6
|
class << self
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# :call-seq:
|
8
|
+
# JSON[object] -> new_array or new_string
|
9
|
+
#
|
10
|
+
# If +object+ is a \String,
|
11
|
+
# calls JSON.parse with +object+ and +opts+ (see method #parse):
|
10
12
|
# json = '[0, 1, null]'
|
11
13
|
# JSON[json]# => [0, 1, nil]
|
12
14
|
#
|
13
|
-
# Otherwise, calls JSON.generate with +object+ and +opts
|
15
|
+
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
|
14
16
|
# ruby = [0, 1, nil]
|
15
17
|
# JSON[ruby] # => '[0,1,null]'
|
16
18
|
def [](object, opts = {})
|
@@ -69,22 +71,30 @@ module JSON
|
|
69
71
|
end
|
70
72
|
self.state = generator::State
|
71
73
|
const_set :State, self.state
|
72
|
-
const_set :SAFE_STATE_PROTOTYPE, State.new
|
73
|
-
const_set :FAST_STATE_PROTOTYPE,
|
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
|
77
|
+
ensure
|
78
|
+
$VERBOSE = old
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_fast_state
|
82
|
+
State.new(
|
74
83
|
:indent => '',
|
75
84
|
:space => '',
|
76
85
|
:object_nl => "",
|
77
86
|
:array_nl => "",
|
78
87
|
:max_nesting => false
|
79
88
|
)
|
80
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_pretty_state
|
92
|
+
State.new(
|
81
93
|
:indent => ' ',
|
82
94
|
:space => ' ',
|
83
95
|
:object_nl => "\n",
|
84
96
|
:array_nl => "\n"
|
85
97
|
)
|
86
|
-
ensure
|
87
|
-
$VERBOSE = old
|
88
98
|
end
|
89
99
|
|
90
100
|
# Returns the JSON generator module that is used by JSON. This is
|
@@ -96,13 +106,26 @@ module JSON
|
|
96
106
|
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
97
107
|
# JSON.state # => JSON::Ext::Generator::State
|
98
108
|
attr_accessor :state
|
109
|
+
end
|
110
|
+
|
111
|
+
DEFAULT_CREATE_ID = 'json_class'.freeze
|
112
|
+
private_constant :DEFAULT_CREATE_ID
|
113
|
+
|
114
|
+
CREATE_ID_TLS_KEY = "JSON.create_id".freeze
|
115
|
+
private_constant :CREATE_ID_TLS_KEY
|
116
|
+
|
117
|
+
# Sets create identifier, which is used to decide if the _json_create_
|
118
|
+
# hook of a class should be called; initial value is +json_class+:
|
119
|
+
# JSON.create_id # => 'json_class'
|
120
|
+
def self.create_id=(new_value)
|
121
|
+
Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
|
122
|
+
end
|
99
123
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
124
|
+
# Returns the current create identifier.
|
125
|
+
# See also JSON.create_id=.
|
126
|
+
def self.create_id
|
127
|
+
Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
|
104
128
|
end
|
105
|
-
self.create_id = 'json_class'
|
106
129
|
|
107
130
|
NaN = 0.0/0
|
108
131
|
|
@@ -144,15 +167,12 @@ module JSON
|
|
144
167
|
# :call-seq:
|
145
168
|
# JSON.parse(source, opts) -> object
|
146
169
|
#
|
147
|
-
#
|
148
|
-
# {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
|
149
|
-
# (implementing +to_str+), and must contain valid \JSON data.
|
170
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
150
171
|
#
|
151
|
-
# Argument +
|
152
|
-
# {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
|
153
|
-
# (implementing +to_hash+).
|
172
|
+
# Argument +source+ contains the \String to be parsed.
|
154
173
|
#
|
155
|
-
#
|
174
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
175
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
156
176
|
#
|
157
177
|
# ---
|
158
178
|
#
|
@@ -171,91 +191,24 @@ module JSON
|
|
171
191
|
# For examples of parsing for all \JSON data types, see
|
172
192
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
173
193
|
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
# Bad value:
|
187
|
-
# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
|
188
|
-
# JSON.parse(source, {max_nesting: :foo})
|
189
|
-
#
|
190
|
-
# ---
|
191
|
-
#
|
192
|
-
# Option +allow_nan+ (boolean) specifies whether to allow
|
193
|
-
# NaN, Infinity, and MinusInfinity in +source+;
|
194
|
-
# defaults to +false+.
|
195
|
-
#
|
196
|
-
# With the default, +false+:
|
197
|
-
# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
|
198
|
-
# JSON.parse('[NaN]')
|
199
|
-
# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
|
200
|
-
# JSON.parse('[Infinity]')
|
201
|
-
# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
|
202
|
-
# JSON.parse('[-Infinity]')
|
203
|
-
# Allow:
|
204
|
-
# source = '[NaN, Infinity, -Infinity]'
|
205
|
-
# ruby = JSON.parse(source, {allow_nan: true})
|
206
|
-
# ruby # => [NaN, Infinity, -Infinity]
|
207
|
-
#
|
208
|
-
# ====== Output Options
|
209
|
-
#
|
210
|
-
# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
|
211
|
-
# should be Symbols;
|
212
|
-
# defaults to +false+ (use Strings).
|
213
|
-
#
|
214
|
-
# With the default, +false+:
|
215
|
-
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
216
|
-
# ruby = JSON.parse(source)
|
217
|
-
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
218
|
-
# Use Symbols:
|
219
|
-
# ruby = JSON.parse(source, {symbolize_names: true})
|
220
|
-
# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
|
221
|
-
#
|
222
|
-
# ---
|
223
|
-
#
|
224
|
-
# Option +object_class+ (\Class) specifies the Ruby class to be used
|
225
|
-
# for each \JSON object;
|
226
|
-
# defaults to \Hash.
|
227
|
-
#
|
228
|
-
# With the default, \Hash:
|
229
|
-
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
230
|
-
# ruby = JSON.parse(source)
|
231
|
-
# ruby.class # => Hash
|
232
|
-
# Use class \OpenStruct:
|
233
|
-
# ruby = JSON.parse(source, {object_class: OpenStruct})
|
234
|
-
# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
|
235
|
-
#
|
236
|
-
# ---
|
237
|
-
#
|
238
|
-
# Option +array_class+ (\Class) specifies the Ruby class to be used
|
239
|
-
# for each \JSON array;
|
240
|
-
# defaults to \Array.
|
241
|
-
#
|
242
|
-
# With the default, \Array:
|
243
|
-
# source = '["foo", 1.0, true, false, null]'
|
194
|
+
# Parses nested JSON objects:
|
195
|
+
# source = <<-EOT
|
196
|
+
# {
|
197
|
+
# "name": "Dave",
|
198
|
+
# "age" :40,
|
199
|
+
# "hats": [
|
200
|
+
# "Cattleman's",
|
201
|
+
# "Panama",
|
202
|
+
# "Tophat"
|
203
|
+
# ]
|
204
|
+
# }
|
205
|
+
# EOT
|
244
206
|
# ruby = JSON.parse(source)
|
245
|
-
# ruby
|
246
|
-
# Use class \Set:
|
247
|
-
# ruby = JSON.parse(source, {array_class: Set})
|
248
|
-
# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
|
207
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
249
208
|
#
|
250
209
|
# ---
|
251
210
|
#
|
252
|
-
# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
|
253
|
-
# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
|
254
|
-
#
|
255
|
-
# ====== Exceptions
|
256
|
-
#
|
257
211
|
# Raises an exception if +source+ is not valid JSON:
|
258
|
-
#
|
259
212
|
# # Raises JSON::ParserError (783: unexpected token at ''):
|
260
213
|
# JSON.parse('')
|
261
214
|
#
|
@@ -283,30 +236,47 @@ module JSON
|
|
283
236
|
end
|
284
237
|
|
285
238
|
# :call-seq:
|
286
|
-
# JSON.
|
239
|
+
# JSON.load_file(path, opts={}) -> object
|
287
240
|
#
|
288
|
-
#
|
241
|
+
# Calls:
|
242
|
+
# parse(File.read(path), opts)
|
289
243
|
#
|
290
|
-
#
|
291
|
-
|
292
|
-
|
244
|
+
# See method #parse.
|
245
|
+
def load_file(filespec, opts = {})
|
246
|
+
parse(File.read(filespec), opts)
|
247
|
+
end
|
248
|
+
|
249
|
+
# :call-seq:
|
250
|
+
# JSON.load_file!(path, opts = {})
|
251
|
+
#
|
252
|
+
# Calls:
|
253
|
+
# JSON.parse!(File.read(path, opts))
|
254
|
+
#
|
255
|
+
# See method #parse!
|
256
|
+
def load_file!(filespec, opts = {})
|
257
|
+
parse!(File.read(filespec), opts)
|
258
|
+
end
|
259
|
+
|
260
|
+
# :call-seq:
|
261
|
+
# JSON.generate(obj, opts = nil) -> new_string
|
293
262
|
#
|
294
263
|
# Returns a \String containing the generated \JSON data.
|
295
264
|
#
|
296
265
|
# See also JSON.fast_generate, JSON.pretty_generate.
|
297
266
|
#
|
267
|
+
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
268
|
+
#
|
269
|
+
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
270
|
+
# See {Generating Options}[#module-JSON-label-Generating+Options].
|
271
|
+
#
|
298
272
|
# ---
|
299
273
|
#
|
300
|
-
# When +obj+ is an
|
301
|
-
# {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects]
|
302
|
-
# (implementing +to_ary+), returns a \String containing a \JSON array:
|
274
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
303
275
|
# obj = ["foo", 1.0, true, false, nil]
|
304
276
|
# json = JSON.generate(obj)
|
305
277
|
# json # => '["foo",1.0,true,false,null]'
|
306
278
|
#
|
307
|
-
# When +obj+ is a
|
308
|
-
# {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects],
|
309
|
-
# return a \String containing a \JSON object:
|
279
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
310
280
|
# obj = {foo: 0, bar: 's', baz: :bat}
|
311
281
|
# json = JSON.generate(obj)
|
312
282
|
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
@@ -314,98 +284,10 @@ module JSON
|
|
314
284
|
# For examples of generating from other Ruby objects, see
|
315
285
|
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
316
286
|
#
|
317
|
-
# ====== Input Options
|
318
|
-
#
|
319
|
-
# Option +allow_nan+ (boolean) specifies whether
|
320
|
-
# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
|
321
|
-
# defaults to +false+.
|
322
|
-
#
|
323
|
-
# With the default, +false+:
|
324
|
-
# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
|
325
|
-
# JSON.generate(JSON::NaN)
|
326
|
-
# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
|
327
|
-
# JSON.generate(JSON::Infinity)
|
328
|
-
# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
|
329
|
-
# JSON.generate(JSON::MinusInfinity)
|
330
|
-
#
|
331
|
-
# Allow:
|
332
|
-
# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
|
333
|
-
# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
|
334
|
-
#
|
335
|
-
# ---
|
336
|
-
#
|
337
|
-
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
|
338
|
-
# in +obj+; defaults to +100+.
|
339
|
-
#
|
340
|
-
# With the default, +100+:
|
341
|
-
# obj = [[[[[[0]]]]]]
|
342
|
-
# JSON.generate(obj) # => '[[[[[[0]]]]]]'
|
343
|
-
#
|
344
|
-
# Too deep:
|
345
|
-
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
346
|
-
# JSON.generate(obj, max_nesting: 2)
|
347
|
-
#
|
348
|
-
# ====== Output Options
|
349
|
-
#
|
350
|
-
# The default formatting options generate the most compact
|
351
|
-
# \JSON data, all on one line and with no whitespace.
|
352
|
-
#
|
353
|
-
# You can use these formatting options to generate
|
354
|
-
# \JSON data in a more open format, using whitespace.
|
355
|
-
# See also JSON.pretty_generate.
|
356
|
-
#
|
357
|
-
# - Option +array_nl+ (\String) specifies a string (usually a newline)
|
358
|
-
# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
|
359
|
-
# - Option +object_nl+ (\String) specifies a string (usually a newline)
|
360
|
-
# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
|
361
|
-
# - Option +indent+ (\String) specifies the string (usually spaces) to be
|
362
|
-
# used for indentation; defaults to the empty \String, <tt>''</tt>;
|
363
|
-
# defaults to the empty \String, <tt>''</tt>;
|
364
|
-
# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
|
365
|
-
# - Option +space+ (\String) specifies a string (usually a space) to be
|
366
|
-
# inserted after the colon in each \JSON object's pair;
|
367
|
-
# defaults to the empty \String, <tt>''</tt>.
|
368
|
-
# - Option +space_before+ (\String) specifies a string (usually a space) to be
|
369
|
-
# inserted before the colon in each \JSON object's pair;
|
370
|
-
# defaults to the empty \String, <tt>''</tt>.
|
371
|
-
#
|
372
|
-
# In this example, +obj+ is used first to generate the shortest
|
373
|
-
# \JSON data (no whitespace), then again with all formatting options
|
374
|
-
# specified:
|
375
|
-
#
|
376
|
-
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
377
|
-
# json = JSON.generate(obj)
|
378
|
-
# puts 'Compact:', json
|
379
|
-
# opts = {
|
380
|
-
# array_nl: "\n",
|
381
|
-
# object_nl: "\n",
|
382
|
-
# indent+: ' ',
|
383
|
-
# space_before: ' ',
|
384
|
-
# space: ' '
|
385
|
-
# }
|
386
|
-
# puts 'Open:', JSON.generate(obj, opts)
|
387
|
-
#
|
388
|
-
# Output:
|
389
|
-
# Compact:
|
390
|
-
# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
|
391
|
-
# Open:
|
392
|
-
# {
|
393
|
-
# "foo" : [
|
394
|
-
# "bar",
|
395
|
-
# "baz"
|
396
|
-
# ],
|
397
|
-
# "bat" : {
|
398
|
-
# "bam" : 0,
|
399
|
-
# "bad" : 1
|
400
|
-
# }
|
401
|
-
# }
|
402
|
-
#
|
403
287
|
# ---
|
404
288
|
#
|
405
289
|
# Raises an exception if any formatting option is not a \String.
|
406
290
|
#
|
407
|
-
# ====== Exceptions
|
408
|
-
#
|
409
291
|
# Raises an exception if +obj+ contains circular references:
|
410
292
|
# a = []; b = []; a.push(b); b.push(a)
|
411
293
|
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
@@ -415,7 +297,7 @@ module JSON
|
|
415
297
|
if State === opts
|
416
298
|
state, opts = opts, nil
|
417
299
|
else
|
418
|
-
state =
|
300
|
+
state = State.new
|
419
301
|
end
|
420
302
|
if opts
|
421
303
|
if opts.respond_to? :to_hash
|
@@ -437,6 +319,9 @@ module JSON
|
|
437
319
|
module_function :unparse
|
438
320
|
# :startdoc:
|
439
321
|
|
322
|
+
# :call-seq:
|
323
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
324
|
+
#
|
440
325
|
# Arguments +obj+ and +opts+ here are the same as
|
441
326
|
# arguments +obj+ and +opts+ in JSON.generate.
|
442
327
|
#
|
@@ -451,7 +336,7 @@ module JSON
|
|
451
336
|
if State === opts
|
452
337
|
state, opts = opts, nil
|
453
338
|
else
|
454
|
-
state =
|
339
|
+
state = JSON.create_fast_state
|
455
340
|
end
|
456
341
|
if opts
|
457
342
|
if opts.respond_to? :to_hash
|
@@ -506,7 +391,7 @@ module JSON
|
|
506
391
|
if State === opts
|
507
392
|
state, opts = opts, nil
|
508
393
|
else
|
509
|
-
state =
|
394
|
+
state = JSON.create_pretty_state
|
510
395
|
end
|
511
396
|
if opts
|
512
397
|
if opts.respond_to? :to_hash
|
@@ -541,20 +426,134 @@ module JSON
|
|
541
426
|
:create_additions => true,
|
542
427
|
}
|
543
428
|
|
544
|
-
#
|
545
|
-
#
|
546
|
-
#
|
547
|
-
#
|
548
|
-
#
|
429
|
+
# :call-seq:
|
430
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
431
|
+
#
|
432
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
433
|
+
#
|
434
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
435
|
+
# - If +source+ responds to instance method +to_str+,
|
436
|
+
# <tt>source.to_str</tt> becomes the source.
|
437
|
+
# - If +source+ responds to instance method +to_io+,
|
438
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
439
|
+
# - If +source+ responds to instance method +read+,
|
440
|
+
# <tt>source.read</tt> becomes the source.
|
441
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
442
|
+
# - Option +allow_blank+ specifies a truthy value.
|
443
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
444
|
+
# - Otherwise, +source+ remains the source.
|
445
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
446
|
+
# It will be called recursively with each result (depth-first order).
|
447
|
+
# 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
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
452
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
453
|
+
# The default options can be changed via method JSON.load_default_options=.
|
454
|
+
#
|
455
|
+
# ---
|
456
|
+
#
|
457
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
458
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
459
|
+
#
|
460
|
+
# Source for following examples:
|
461
|
+
# source = <<-EOT
|
462
|
+
# {
|
463
|
+
# "name": "Dave",
|
464
|
+
# "age" :40,
|
465
|
+
# "hats": [
|
466
|
+
# "Cattleman's",
|
467
|
+
# "Panama",
|
468
|
+
# "Tophat"
|
469
|
+
# ]
|
470
|
+
# }
|
471
|
+
# EOT
|
472
|
+
#
|
473
|
+
# Load a \String:
|
474
|
+
# ruby = JSON.load(source)
|
475
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
476
|
+
#
|
477
|
+
# Load an \IO object:
|
478
|
+
# require 'stringio'
|
479
|
+
# object = JSON.load(StringIO.new(source))
|
480
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
481
|
+
#
|
482
|
+
# Load a \File object:
|
483
|
+
# path = 't.json'
|
484
|
+
# File.write(path, source)
|
485
|
+
# File.open(path) do |file|
|
486
|
+
# JSON.load(file)
|
487
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
549
488
|
#
|
550
|
-
#
|
551
|
-
#
|
552
|
-
#
|
553
|
-
#
|
554
|
-
#
|
489
|
+
# ---
|
490
|
+
#
|
491
|
+
# When +proc+ is given:
|
492
|
+
# - Modifies +source+ as above.
|
493
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
494
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
495
|
+
# - Returns the final result.
|
496
|
+
#
|
497
|
+
# Example:
|
498
|
+
# require 'json'
|
499
|
+
#
|
500
|
+
# # Some classes for the example.
|
501
|
+
# class Base
|
502
|
+
# def initialize(attributes)
|
503
|
+
# @attributes = attributes
|
504
|
+
# end
|
505
|
+
# end
|
506
|
+
# class User < Base; end
|
507
|
+
# class Account < Base; end
|
508
|
+
# class Admin < Base; end
|
509
|
+
# # The JSON source.
|
510
|
+
# json = <<-EOF
|
511
|
+
# {
|
512
|
+
# "users": [
|
513
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
514
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
515
|
+
# ],
|
516
|
+
# "accounts": [
|
517
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
518
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
519
|
+
# ],
|
520
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
521
|
+
# }
|
522
|
+
# EOF
|
523
|
+
# # Deserializer method.
|
524
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
525
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
526
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
527
|
+
# end
|
528
|
+
# # Call to JSON.load
|
529
|
+
# ruby = JSON.load(json, proc {|obj|
|
530
|
+
# case obj
|
531
|
+
# when Hash
|
532
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
533
|
+
# when Array
|
534
|
+
# obj.map! {|v| deserialize_obj v }
|
535
|
+
# end
|
536
|
+
# })
|
537
|
+
# pp ruby
|
538
|
+
# Output:
|
539
|
+
# {"users"=>
|
540
|
+
# [#<User:0x00000000064c4c98
|
541
|
+
# @attributes=
|
542
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
543
|
+
# #<User:0x00000000064c4bd0
|
544
|
+
# @attributes=
|
545
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
546
|
+
# "accounts"=>
|
547
|
+
# [{"account"=>
|
548
|
+
# #<Account:0x00000000064c4928
|
549
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
550
|
+
# {"account"=>
|
551
|
+
# #<Account:0x00000000064c4680
|
552
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
553
|
+
# "admins"=>
|
554
|
+
# #<Admin:0x00000000064c41f8
|
555
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
555
556
|
#
|
556
|
-
# This method is part of the implementation of the load/dump interface of
|
557
|
-
# Marshal and YAML.
|
558
557
|
def load(source, proc = nil, options = {})
|
559
558
|
opts = load_default_options.merge options
|
560
559
|
if source.respond_to? :to_str
|
@@ -573,7 +572,7 @@ module JSON
|
|
573
572
|
end
|
574
573
|
|
575
574
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
576
|
-
def recurse_proc(result, &proc)
|
575
|
+
def recurse_proc(result, &proc) # :nodoc:
|
577
576
|
case result
|
578
577
|
when Array
|
579
578
|
result.each { |x| recurse_proc x, &proc }
|
@@ -593,29 +592,42 @@ module JSON
|
|
593
592
|
# Sets or returns the default options for the JSON.dump method.
|
594
593
|
# Initially:
|
595
594
|
# opts = JSON.dump_default_options
|
596
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
595
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
597
596
|
attr_accessor :dump_default_options
|
598
597
|
end
|
599
598
|
self.dump_default_options = {
|
600
599
|
:max_nesting => false,
|
601
600
|
:allow_nan => true,
|
601
|
+
:escape_slash => false,
|
602
602
|
}
|
603
603
|
|
604
|
-
#
|
605
|
-
#
|
604
|
+
# :call-seq:
|
605
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
606
606
|
#
|
607
|
-
#
|
608
|
-
# was given, the resulting JSON is written to it.
|
607
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
609
608
|
#
|
610
|
-
#
|
611
|
-
# exception is raised. This argument is similar (but not exactly the
|
612
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
609
|
+
# The default options can be changed via method JSON.dump_default_options.
|
613
610
|
#
|
614
|
-
#
|
615
|
-
#
|
611
|
+
# - Argument +io+, if given, should respond to method +write+;
|
612
|
+
# the \JSON \String is written to +io+, and +io+ is returned.
|
613
|
+
# If +io+ is not given, the \JSON \String is returned.
|
614
|
+
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
616
615
|
#
|
617
|
-
#
|
618
|
-
#
|
616
|
+
# ---
|
617
|
+
#
|
618
|
+
# When argument +io+ is not given, returns the \JSON \String generated from +obj+:
|
619
|
+
# obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
|
620
|
+
# json = JSON.dump(obj)
|
621
|
+
# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
|
622
|
+
#
|
623
|
+
# When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
|
624
|
+
# path = 't.json'
|
625
|
+
# File.open(path, 'w') do |file|
|
626
|
+
# JSON.dump(obj, file)
|
627
|
+
# end # => #<File:t.json (closed)>
|
628
|
+
# puts File.read(path)
|
629
|
+
# Output:
|
630
|
+
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
619
631
|
def dump(obj, anIO = nil, limit = nil)
|
620
632
|
if anIO and limit.nil?
|
621
633
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|