json 1.8.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.travis.yml +9 -12
- data/{CHANGES → CHANGES.md} +219 -90
- data/Gemfile +10 -6
- data/{COPYING-json-jruby → LICENSE} +5 -6
- data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
- data/{README.rdoc → README.md} +201 -134
- data/Rakefile +35 -113
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +255 -101
- data/ext/json/ext/generator/generator.h +12 -4
- data/ext/json/ext/parser/extconf.rb +28 -0
- data/ext/json/ext/parser/parser.c +410 -462
- data/ext/json/ext/parser/parser.h +5 -5
- data/ext/json/ext/parser/parser.rl +166 -181
- data/ext/json/extconf.rb +1 -1
- data/java/src/json/ext/ByteListTranscoder.java +1 -2
- data/java/src/json/ext/Generator.java +39 -36
- data/java/src/json/ext/GeneratorMethods.java +1 -2
- data/java/src/json/ext/GeneratorService.java +1 -2
- data/java/src/json/ext/GeneratorState.java +33 -56
- data/java/src/json/ext/OptionsReader.java +2 -3
- data/java/src/json/ext/Parser.java +146 -417
- data/java/src/json/ext/Parser.rl +62 -126
- data/java/src/json/ext/ParserService.java +1 -2
- data/java/src/json/ext/RuntimeInfo.java +1 -6
- data/java/src/json/ext/StringDecoder.java +1 -2
- data/java/src/json/ext/StringEncoder.java +13 -2
- data/java/src/json/ext/Utils.java +1 -2
- data/json-java.gemspec +22 -7
- data/json.gemspec +0 -0
- data/json_pure.gemspec +22 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -4
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -3
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +350 -152
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure/generator.rb +83 -126
- data/lib/json/pure/parser.rb +62 -84
- data/lib/json/pure.rb +2 -8
- data/lib/json/version.rb +2 -1
- data/lib/json.rb +550 -29
- data/references/rfc7159.txt +899 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
- data/tests/json_common_interface_test.rb +169 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +13 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +134 -39
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +497 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/test_helper.rb +17 -0
- data/tools/diff.sh +18 -0
- data/tools/fuzz.rb +1 -9
- metadata +47 -53
- data/COPYING +0 -58
- data/GPL +0 -340
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/tests/fixtures/fail1.json +0 -1
- data/tests/setup_variant.rb +0 -11
- data/tests/test_json.rb +0 -553
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
data/lib/json/common.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
require 'json/version'
|
2
3
|
require 'json/generic_object'
|
3
4
|
|
4
5
|
module JSON
|
5
6
|
class << self
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# data structure object and return it.
|
7
|
+
# :call-seq:
|
8
|
+
# JSON[object] -> new_array or new_string
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# If +object+ is a \String,
|
11
|
+
# calls JSON.parse with +object+ and +opts+ (see method #parse):
|
12
|
+
# json = '[0, 1, null]'
|
13
|
+
# JSON[json]# => [0, 1, nil]
|
14
|
+
#
|
15
|
+
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
|
16
|
+
# ruby = [0, 1, nil]
|
17
|
+
# JSON[ruby] # => '[0,1,null]'
|
12
18
|
def [](object, opts = {})
|
13
19
|
if object.respond_to? :to_str
|
14
20
|
JSON.parse(object.to_str, opts)
|
@@ -18,13 +24,14 @@ module JSON
|
|
18
24
|
end
|
19
25
|
|
20
26
|
# Returns the JSON parser class that is used by JSON. This is either
|
21
|
-
# JSON::Ext::Parser or JSON::Pure::Parser
|
27
|
+
# JSON::Ext::Parser or JSON::Pure::Parser:
|
28
|
+
# JSON.parser # => JSON::Ext::Parser
|
22
29
|
attr_reader :parser
|
23
30
|
|
24
31
|
# Set the JSON parser class _parser_ to be used by JSON.
|
25
32
|
def parser=(parser) # :nodoc:
|
26
33
|
@parser = parser
|
27
|
-
remove_const :Parser if
|
34
|
+
remove_const :Parser if const_defined?(:Parser, false)
|
28
35
|
const_set :Parser, parser
|
29
36
|
end
|
30
37
|
|
@@ -35,8 +42,8 @@ module JSON
|
|
35
42
|
def deep_const_get(path) # :nodoc:
|
36
43
|
path.to_s.split(/::/).inject(Object) do |p, c|
|
37
44
|
case
|
38
|
-
when c.empty?
|
39
|
-
when
|
45
|
+
when c.empty? then p
|
46
|
+
when p.const_defined?(c, true) then p.const_get(c)
|
40
47
|
else
|
41
48
|
begin
|
42
49
|
p.const_missing(c)
|
@@ -83,15 +90,18 @@ module JSON
|
|
83
90
|
end
|
84
91
|
|
85
92
|
# Returns the JSON generator module that is used by JSON. This is
|
86
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator
|
93
|
+
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
94
|
+
# JSON.generator # => JSON::Ext::Generator
|
87
95
|
attr_reader :generator
|
88
96
|
|
89
|
-
# Returns the JSON generator state class that is used by JSON. This is
|
90
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State
|
97
|
+
# Sets or Returns the JSON generator state class that is used by JSON. This is
|
98
|
+
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
99
|
+
# JSON.state # => JSON::Ext::Generator::State
|
91
100
|
attr_accessor :state
|
92
101
|
|
93
|
-
#
|
94
|
-
# hook of a class should be called
|
102
|
+
# Sets or returns create identifier, which is used to decide if the _json_create_
|
103
|
+
# hook of a class should be called; initial value is +json_class+:
|
104
|
+
# JSON.create_id # => 'json_class'
|
95
105
|
attr_accessor :create_id
|
96
106
|
end
|
97
107
|
self.create_id = 'json_class'
|
@@ -125,7 +135,7 @@ module JSON
|
|
125
135
|
# This exception is raised if a generator or unparser error occurs.
|
126
136
|
class GeneratorError < JSONError; end
|
127
137
|
# For backwards compatibility
|
128
|
-
UnparserError = GeneratorError
|
138
|
+
UnparserError = GeneratorError # :nodoc:
|
129
139
|
|
130
140
|
# This exception is raised if the required unicode support is missing on the
|
131
141
|
# system. Usually this means that the iconv library is not installed.
|
@@ -133,77 +143,135 @@ module JSON
|
|
133
143
|
|
134
144
|
module_function
|
135
145
|
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
146
|
+
# :call-seq:
|
147
|
+
# JSON.parse(source, opts) -> object
|
148
|
+
#
|
149
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
150
|
+
#
|
151
|
+
# Argument +source+ contains the \String to be parsed.
|
152
|
+
#
|
153
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
154
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
155
|
+
#
|
156
|
+
# ---
|
157
|
+
#
|
158
|
+
# When +source+ is a \JSON array, returns a Ruby \Array:
|
159
|
+
# source = '["foo", 1.0, true, false, null]'
|
160
|
+
# ruby = JSON.parse(source)
|
161
|
+
# ruby # => ["foo", 1.0, true, false, nil]
|
162
|
+
# ruby.class # => Array
|
163
|
+
#
|
164
|
+
# When +source+ is a \JSON object, returns a Ruby \Hash:
|
165
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
166
|
+
# ruby = JSON.parse(source)
|
167
|
+
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
168
|
+
# ruby.class # => Hash
|
169
|
+
#
|
170
|
+
# For examples of parsing for all \JSON data types, see
|
171
|
+
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
172
|
+
#
|
173
|
+
# Parses nested JSON objects:
|
174
|
+
# source = <<-EOT
|
175
|
+
# {
|
176
|
+
# "name": "Dave",
|
177
|
+
# "age" :40,
|
178
|
+
# "hats": [
|
179
|
+
# "Cattleman's",
|
180
|
+
# "Panama",
|
181
|
+
# "Tophat"
|
182
|
+
# ]
|
183
|
+
# }
|
184
|
+
# EOT
|
185
|
+
# ruby = JSON.parse(source)
|
186
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
187
|
+
#
|
188
|
+
# ---
|
189
|
+
#
|
190
|
+
# Raises an exception if +source+ is not valid JSON:
|
191
|
+
# # Raises JSON::ParserError (783: unexpected token at ''):
|
192
|
+
# JSON.parse('')
|
193
|
+
#
|
154
194
|
def parse(source, opts = {})
|
155
|
-
Parser.new(source, opts).parse
|
195
|
+
Parser.new(source, **(opts||{})).parse
|
156
196
|
end
|
157
197
|
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
# to true.
|
170
|
-
# * *create_additions*: If set to false, the Parser doesn't create
|
171
|
-
# additions even if a matching class and create_id was found. This option
|
172
|
-
# defaults to false.
|
198
|
+
# :call-seq:
|
199
|
+
# JSON.parse!(source, opts) -> object
|
200
|
+
#
|
201
|
+
# Calls
|
202
|
+
# parse(source, opts)
|
203
|
+
# with +source+ and possibly modified +opts+.
|
204
|
+
#
|
205
|
+
# Differences from JSON.parse:
|
206
|
+
# - Option +max_nesting+, if not provided, defaults to +false+,
|
207
|
+
# which disables checking for nesting depth.
|
208
|
+
# - Option +allow_nan+, if not provided, defaults to +true+.
|
173
209
|
def parse!(source, opts = {})
|
174
210
|
opts = {
|
175
211
|
:max_nesting => false,
|
176
212
|
:allow_nan => true
|
177
|
-
}.
|
178
|
-
Parser.new(source, opts).parse
|
213
|
+
}.merge(opts)
|
214
|
+
Parser.new(source, **(opts||{})).parse
|
179
215
|
end
|
180
216
|
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
217
|
+
# :call-seq:
|
218
|
+
# JSON.load_file(path, opts={}) -> object
|
219
|
+
#
|
220
|
+
# Calls:
|
221
|
+
# parse(File.read(path), opts)
|
222
|
+
#
|
223
|
+
# See method #parse.
|
224
|
+
def load_file(filespec, opts = {})
|
225
|
+
parse(File.read(filespec), opts)
|
226
|
+
end
|
227
|
+
|
228
|
+
# :call-seq:
|
229
|
+
# JSON.load_file!(path, opts = {})
|
230
|
+
#
|
231
|
+
# Calls:
|
232
|
+
# JSON.parse!(File.read(path, opts))
|
233
|
+
#
|
234
|
+
# See method #parse!
|
235
|
+
def load_file!(filespec, opts = {})
|
236
|
+
parse!(File.read(filespec), opts)
|
237
|
+
end
|
238
|
+
|
239
|
+
# :call-seq:
|
240
|
+
# JSON.generate(obj, opts = nil) -> new_string
|
241
|
+
#
|
242
|
+
# Returns a \String containing the generated \JSON data.
|
243
|
+
#
|
244
|
+
# See also JSON.fast_generate, JSON.pretty_generate.
|
245
|
+
#
|
246
|
+
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
247
|
+
#
|
248
|
+
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
249
|
+
# See {Generating Options}[#module-JSON-label-Generating+Options].
|
250
|
+
#
|
251
|
+
# ---
|
252
|
+
#
|
253
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
254
|
+
# obj = ["foo", 1.0, true, false, nil]
|
255
|
+
# json = JSON.generate(obj)
|
256
|
+
# json # => '["foo",1.0,true,false,null]'
|
257
|
+
#
|
258
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
259
|
+
# obj = {foo: 0, bar: 's', baz: :bat}
|
260
|
+
# json = JSON.generate(obj)
|
261
|
+
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
262
|
+
#
|
263
|
+
# For examples of generating from other Ruby objects, see
|
264
|
+
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
265
|
+
#
|
266
|
+
# ---
|
267
|
+
#
|
268
|
+
# Raises an exception if any formatting option is not a \String.
|
269
|
+
#
|
270
|
+
# Raises an exception if +obj+ contains circular references:
|
271
|
+
# a = []; b = []; a.push(b); b.push(a)
|
272
|
+
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
273
|
+
# JSON.generate(a)
|
274
|
+
#
|
207
275
|
def generate(obj, opts = nil)
|
208
276
|
if State === opts
|
209
277
|
state, opts = opts, nil
|
@@ -230,11 +298,19 @@ module JSON
|
|
230
298
|
module_function :unparse
|
231
299
|
# :startdoc:
|
232
300
|
|
233
|
-
#
|
234
|
-
#
|
301
|
+
# :call-seq:
|
302
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
303
|
+
#
|
304
|
+
# Arguments +obj+ and +opts+ here are the same as
|
305
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
235
306
|
#
|
236
|
-
#
|
237
|
-
#
|
307
|
+
# By default, generates \JSON data without checking
|
308
|
+
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
309
|
+
#
|
310
|
+
# Raises an exception if +obj+ contains circular references:
|
311
|
+
# a = []; b = []; a.push(b); b.push(a)
|
312
|
+
# # Raises SystemStackError (stack level too deep):
|
313
|
+
# JSON.fast_generate(a)
|
238
314
|
def fast_generate(obj, opts = nil)
|
239
315
|
if State === opts
|
240
316
|
state, opts = opts, nil
|
@@ -260,12 +336,36 @@ module JSON
|
|
260
336
|
module_function :fast_unparse
|
261
337
|
# :startdoc:
|
262
338
|
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
339
|
+
# :call-seq:
|
340
|
+
# JSON.pretty_generate(obj, opts = nil) -> new_string
|
341
|
+
#
|
342
|
+
# Arguments +obj+ and +opts+ here are the same as
|
343
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
344
|
+
#
|
345
|
+
# Default options are:
|
346
|
+
# {
|
347
|
+
# indent: ' ', # Two spaces
|
348
|
+
# space: ' ', # One space
|
349
|
+
# array_nl: "\n", # Newline
|
350
|
+
# object_nl: "\n" # Newline
|
351
|
+
# }
|
352
|
+
#
|
353
|
+
# Example:
|
354
|
+
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
355
|
+
# json = JSON.pretty_generate(obj)
|
356
|
+
# puts json
|
357
|
+
# Output:
|
358
|
+
# {
|
359
|
+
# "foo": [
|
360
|
+
# "bar",
|
361
|
+
# "baz"
|
362
|
+
# ],
|
363
|
+
# "bat": {
|
364
|
+
# "bam": 0,
|
365
|
+
# "bad": 1
|
366
|
+
# }
|
367
|
+
# }
|
266
368
|
#
|
267
|
-
# The _opts_ argument can be used to configure the generator. See the
|
268
|
-
# generate method for a more detailed explanation.
|
269
369
|
def pretty_generate(obj, opts = nil)
|
270
370
|
if State === opts
|
271
371
|
state, opts = opts, nil
|
@@ -292,33 +392,147 @@ module JSON
|
|
292
392
|
# :startdoc:
|
293
393
|
|
294
394
|
class << self
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
395
|
+
# Sets or returns default options for the JSON.load method.
|
396
|
+
# Initially:
|
397
|
+
# opts = JSON.load_default_options
|
398
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
299
399
|
attr_accessor :load_default_options
|
300
400
|
end
|
301
401
|
self.load_default_options = {
|
302
402
|
:max_nesting => false,
|
303
403
|
:allow_nan => true,
|
304
|
-
:
|
404
|
+
:allow_blank => true,
|
305
405
|
:create_additions => true,
|
306
406
|
}
|
307
407
|
|
308
|
-
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
408
|
+
# :call-seq:
|
409
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
410
|
+
#
|
411
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
412
|
+
#
|
413
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
414
|
+
# - If +source+ responds to instance method +to_str+,
|
415
|
+
# <tt>source.to_str</tt> becomes the source.
|
416
|
+
# - If +source+ responds to instance method +to_io+,
|
417
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
418
|
+
# - If +source+ responds to instance method +read+,
|
419
|
+
# <tt>source.read</tt> becomes the source.
|
420
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
421
|
+
# - Option +allow_blank+ specifies a truthy value.
|
422
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
423
|
+
# - Otherwise, +source+ remains the source.
|
424
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
425
|
+
# It will be called recursively with each result (depth-first order).
|
426
|
+
# See details below.
|
427
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
428
|
+
# like from your own database server or clients under your control, it could
|
429
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
430
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
431
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
432
|
+
# The default options can be changed via method JSON.load_default_options=.
|
433
|
+
#
|
434
|
+
# ---
|
435
|
+
#
|
436
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
437
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
438
|
+
#
|
439
|
+
# Source for following examples:
|
440
|
+
# source = <<-EOT
|
441
|
+
# {
|
442
|
+
# "name": "Dave",
|
443
|
+
# "age" :40,
|
444
|
+
# "hats": [
|
445
|
+
# "Cattleman's",
|
446
|
+
# "Panama",
|
447
|
+
# "Tophat"
|
448
|
+
# ]
|
449
|
+
# }
|
450
|
+
# EOT
|
451
|
+
#
|
452
|
+
# Load a \String:
|
453
|
+
# ruby = JSON.load(source)
|
454
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
455
|
+
#
|
456
|
+
# Load an \IO object:
|
457
|
+
# require 'stringio'
|
458
|
+
# object = JSON.load(StringIO.new(source))
|
459
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
313
460
|
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
461
|
+
# Load a \File object:
|
462
|
+
# path = 't.json'
|
463
|
+
# File.write(path, source)
|
464
|
+
# File.open(path) do |file|
|
465
|
+
# JSON.load(file)
|
466
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
467
|
+
#
|
468
|
+
# ---
|
469
|
+
#
|
470
|
+
# When +proc+ is given:
|
471
|
+
# - Modifies +source+ as above.
|
472
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
473
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
474
|
+
# - Returns the final result.
|
475
|
+
#
|
476
|
+
# Example:
|
477
|
+
# require 'json'
|
478
|
+
#
|
479
|
+
# # Some classes for the example.
|
480
|
+
# class Base
|
481
|
+
# def initialize(attributes)
|
482
|
+
# @attributes = attributes
|
483
|
+
# end
|
484
|
+
# end
|
485
|
+
# class User < Base; end
|
486
|
+
# class Account < Base; end
|
487
|
+
# class Admin < Base; end
|
488
|
+
# # The JSON source.
|
489
|
+
# json = <<-EOF
|
490
|
+
# {
|
491
|
+
# "users": [
|
492
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
493
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
494
|
+
# ],
|
495
|
+
# "accounts": [
|
496
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
497
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
498
|
+
# ],
|
499
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
500
|
+
# }
|
501
|
+
# EOF
|
502
|
+
# # Deserializer method.
|
503
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
504
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
505
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
506
|
+
# end
|
507
|
+
# # Call to JSON.load
|
508
|
+
# ruby = JSON.load(json, proc {|obj|
|
509
|
+
# case obj
|
510
|
+
# when Hash
|
511
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
512
|
+
# when Array
|
513
|
+
# obj.map! {|v| deserialize_obj v }
|
514
|
+
# end
|
515
|
+
# })
|
516
|
+
# pp ruby
|
517
|
+
# Output:
|
518
|
+
# {"users"=>
|
519
|
+
# [#<User:0x00000000064c4c98
|
520
|
+
# @attributes=
|
521
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
522
|
+
# #<User:0x00000000064c4bd0
|
523
|
+
# @attributes=
|
524
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
525
|
+
# "accounts"=>
|
526
|
+
# [{"account"=>
|
527
|
+
# #<Account:0x00000000064c4928
|
528
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
529
|
+
# {"account"=>
|
530
|
+
# #<Account:0x00000000064c4680
|
531
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
532
|
+
# "admins"=>
|
533
|
+
# #<Admin:0x00000000064c41f8
|
534
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
319
535
|
#
|
320
|
-
# This method is part of the implementation of the load/dump interface of
|
321
|
-
# Marshal and YAML.
|
322
536
|
def load(source, proc = nil, options = {})
|
323
537
|
opts = load_default_options.merge options
|
324
538
|
if source.respond_to? :to_str
|
@@ -328,7 +542,7 @@ module JSON
|
|
328
542
|
elsif source.respond_to?(:read)
|
329
543
|
source = source.read
|
330
544
|
end
|
331
|
-
if opts[:
|
545
|
+
if opts[:allow_blank] && (source.nil? || source.empty?)
|
332
546
|
source = 'null'
|
333
547
|
end
|
334
548
|
result = parse(source, opts)
|
@@ -337,7 +551,7 @@ module JSON
|
|
337
551
|
end
|
338
552
|
|
339
553
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
340
|
-
def recurse_proc(result, &proc)
|
554
|
+
def recurse_proc(result, &proc) # :nodoc:
|
341
555
|
case result
|
342
556
|
when Array
|
343
557
|
result.each { |x| recurse_proc x, &proc }
|
@@ -354,33 +568,45 @@ module JSON
|
|
354
568
|
module_function :restore
|
355
569
|
|
356
570
|
class << self
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
571
|
+
# Sets or returns the default options for the JSON.dump method.
|
572
|
+
# Initially:
|
573
|
+
# opts = JSON.dump_default_options
|
574
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
361
575
|
attr_accessor :dump_default_options
|
362
576
|
end
|
363
577
|
self.dump_default_options = {
|
364
578
|
:max_nesting => false,
|
365
579
|
:allow_nan => true,
|
366
|
-
:
|
580
|
+
:escape_slash => false,
|
367
581
|
}
|
368
582
|
|
369
|
-
#
|
370
|
-
#
|
583
|
+
# :call-seq:
|
584
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
371
585
|
#
|
372
|
-
#
|
373
|
-
# was given, the resulting JSON is written to it.
|
586
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
374
587
|
#
|
375
|
-
#
|
376
|
-
# exception is raised. This argument is similar (but not exactly the
|
377
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
588
|
+
# The default options can be changed via method JSON.dump_default_options.
|
378
589
|
#
|
379
|
-
#
|
380
|
-
#
|
590
|
+
# - Argument +io+, if given, should respond to method +write+;
|
591
|
+
# the \JSON \String is written to +io+, and +io+ is returned.
|
592
|
+
# If +io+ is not given, the \JSON \String is returned.
|
593
|
+
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
381
594
|
#
|
382
|
-
#
|
383
|
-
#
|
595
|
+
# ---
|
596
|
+
#
|
597
|
+
# When argument +io+ is not given, returns the \JSON \String generated from +obj+:
|
598
|
+
# obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
|
599
|
+
# json = JSON.dump(obj)
|
600
|
+
# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
|
601
|
+
#
|
602
|
+
# When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
|
603
|
+
# path = 't.json'
|
604
|
+
# File.open(path, 'w') do |file|
|
605
|
+
# JSON.dump(obj, file)
|
606
|
+
# end # => #<File:t.json (closed)>
|
607
|
+
# puts File.read(path)
|
608
|
+
# Output:
|
609
|
+
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
384
610
|
def dump(obj, anIO = nil, limit = nil)
|
385
611
|
if anIO and limit.nil?
|
386
612
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
@@ -402,37 +628,9 @@ module JSON
|
|
402
628
|
raise ArgumentError, "exceed depth limit"
|
403
629
|
end
|
404
630
|
|
405
|
-
#
|
406
|
-
def self.
|
407
|
-
|
408
|
-
break unless string[2 * i + 1]
|
409
|
-
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
|
410
|
-
end
|
411
|
-
string
|
412
|
-
end
|
413
|
-
|
414
|
-
# Shortcut for iconv.
|
415
|
-
if ::String.method_defined?(:encode)
|
416
|
-
# Encodes string using Ruby's _String.encode_
|
417
|
-
def self.iconv(to, from, string)
|
418
|
-
string.encode(to, from)
|
419
|
-
end
|
420
|
-
else
|
421
|
-
require 'iconv'
|
422
|
-
# Encodes string using _iconv_ library
|
423
|
-
def self.iconv(to, from, string)
|
424
|
-
Iconv.conv(to, from, string)
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
if ::Object.method(:const_defined?).arity == 1
|
429
|
-
def self.const_defined_in?(modul, constant)
|
430
|
-
modul.const_defined?(constant)
|
431
|
-
end
|
432
|
-
else
|
433
|
-
def self.const_defined_in?(modul, constant)
|
434
|
-
modul.const_defined?(constant, false)
|
435
|
-
end
|
631
|
+
# Encodes string using String.encode.
|
632
|
+
def self.iconv(to, from, string)
|
633
|
+
string.encode(to, from)
|
436
634
|
end
|
437
635
|
end
|
438
636
|
|
data/lib/json/ext.rb
CHANGED
data/lib/json/generic_object.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
require 'ostruct'
|
2
3
|
|
3
4
|
module JSON
|
@@ -48,12 +49,12 @@ module JSON
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def [](name)
|
51
|
-
|
52
|
-
end
|
52
|
+
__send__(name)
|
53
|
+
end unless method_defined?(:[])
|
53
54
|
|
54
55
|
def []=(name, value)
|
55
|
-
__send__
|
56
|
-
end
|
56
|
+
__send__("#{name}=", value)
|
57
|
+
end unless method_defined?(:[]=)
|
57
58
|
|
58
59
|
def |(other)
|
59
60
|
self.class[other.to_hash.merge(to_hash)]
|