json 2.2.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +51 -0
- data/LICENSE +56 -0
- data/README.md +17 -1
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +222 -48
- 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 +150 -102
- data/ext/json/ext/parser/parser.h +1 -0
- data/ext/json/ext/parser/parser.rl +80 -32
- data/ext/json/extconf.rb +1 -0
- data/json.gemspec +0 -0
- data/lib/json.rb +549 -29
- data/lib/json/add/bigdecimal.rb +2 -2
- data/lib/json/add/complex.rb +2 -3
- data/lib/json/add/rational.rb +2 -3
- data/lib/json/add/regexp.rb +2 -2
- data/lib/json/common.rb +370 -125
- data/lib/json/pure/generator.rb +31 -10
- data/lib/json/pure/parser.rb +31 -5
- 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 +47 -4
- data/tests/json_fixtures_test.rb +9 -1
- data/tests/json_generator_test.rb +30 -8
- data/tests/json_parser_test.rb +39 -14
- 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 +30 -40
- data/.gitignore +0 -17
- data/.travis.yml +0 -23
- data/README-json-jruby.md +0 -33
- data/Rakefile +0 -408
- 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 -443
- 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 -38
- data/json_pure.gemspec +0 -38
- 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/bigdecimal.rb
CHANGED
data/lib/json/add/complex.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
3
3
|
require 'json'
|
4
4
|
end
|
5
|
-
defined?(::Complex) or require 'complex'
|
6
5
|
|
7
6
|
class Complex
|
8
7
|
|
@@ -23,7 +22,7 @@ class Complex
|
|
23
22
|
end
|
24
23
|
|
25
24
|
# Stores class name (Complex) along with real value <tt>r</tt> and imaginary value <tt>i</tt> as JSON string
|
26
|
-
def to_json(*)
|
27
|
-
as_json.to_json
|
25
|
+
def to_json(*args)
|
26
|
+
as_json.to_json(*args)
|
28
27
|
end
|
29
28
|
end
|
data/lib/json/add/rational.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
|
3
3
|
require 'json'
|
4
4
|
end
|
5
|
-
defined?(::Rational) or require 'rational'
|
6
5
|
|
7
6
|
class Rational
|
8
7
|
# Deserializes JSON string by converting numerator value <tt>n</tt>,
|
@@ -22,7 +21,7 @@ class Rational
|
|
22
21
|
end
|
23
22
|
|
24
23
|
# Stores class name (Rational) along with numerator value <tt>n</tt> and denominator value <tt>d</tt> as JSON string
|
25
|
-
def to_json(*)
|
26
|
-
as_json.to_json
|
24
|
+
def to_json(*args)
|
25
|
+
as_json.to_json(*args)
|
27
26
|
end
|
28
27
|
end
|
data/lib/json/add/regexp.rb
CHANGED
data/lib/json/common.rb
CHANGED
@@ -4,12 +4,17 @@ require 'json/generic_object'
|
|
4
4
|
|
5
5
|
module JSON
|
6
6
|
class << self
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# Ruby data structure object and return it.
|
7
|
+
# :call-seq:
|
8
|
+
# JSON[object] -> new_array or new_string
|
10
9
|
#
|
11
|
-
#
|
12
|
-
#
|
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]'
|
13
18
|
def [](object, opts = {})
|
14
19
|
if object.respond_to? :to_str
|
15
20
|
JSON.parse(object.to_str, opts)
|
@@ -19,7 +24,8 @@ module JSON
|
|
19
24
|
end
|
20
25
|
|
21
26
|
# Returns the JSON parser class that is used by JSON. This is either
|
22
|
-
# JSON::Ext::Parser or JSON::Pure::Parser
|
27
|
+
# JSON::Ext::Parser or JSON::Pure::Parser:
|
28
|
+
# JSON.parser # => JSON::Ext::Parser
|
23
29
|
attr_reader :parser
|
24
30
|
|
25
31
|
# Set the JSON parser class _parser_ to be used by JSON.
|
@@ -65,37 +71,59 @@ module JSON
|
|
65
71
|
end
|
66
72
|
self.state = generator::State
|
67
73
|
const_set :State, self.state
|
68
|
-
const_set :SAFE_STATE_PROTOTYPE, State.new
|
69
|
-
|
74
|
+
const_set :SAFE_STATE_PROTOTYPE, State.new # for JRuby
|
75
|
+
ensure
|
76
|
+
$VERBOSE = old
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_fast_state
|
80
|
+
State.new(
|
70
81
|
:indent => '',
|
71
82
|
:space => '',
|
72
83
|
:object_nl => "",
|
73
84
|
:array_nl => "",
|
74
85
|
:max_nesting => false
|
75
86
|
)
|
76
|
-
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_pretty_state
|
90
|
+
State.new(
|
77
91
|
:indent => ' ',
|
78
92
|
:space => ' ',
|
79
93
|
:object_nl => "\n",
|
80
94
|
:array_nl => "\n"
|
81
95
|
)
|
82
|
-
ensure
|
83
|
-
$VERBOSE = old
|
84
96
|
end
|
85
97
|
|
86
98
|
# Returns the JSON generator module that is used by JSON. This is
|
87
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator
|
99
|
+
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
100
|
+
# JSON.generator # => JSON::Ext::Generator
|
88
101
|
attr_reader :generator
|
89
102
|
|
90
|
-
# Returns the JSON generator state class that is used by JSON. This is
|
91
|
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State
|
103
|
+
# Sets or Returns the JSON generator state class that is used by JSON. This is
|
104
|
+
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
105
|
+
# JSON.state # => JSON::Ext::Generator::State
|
92
106
|
attr_accessor :state
|
107
|
+
end
|
108
|
+
|
109
|
+
DEFAULT_CREATE_ID = 'json_class'.freeze
|
110
|
+
private_constant :DEFAULT_CREATE_ID
|
111
|
+
|
112
|
+
CREATE_ID_TLS_KEY = "JSON.create_id".freeze
|
113
|
+
private_constant :CREATE_ID_TLS_KEY
|
114
|
+
|
115
|
+
# Sets create identifier, which is used to decide if the _json_create_
|
116
|
+
# hook of a class should be called; initial value is +json_class+:
|
117
|
+
# JSON.create_id # => 'json_class'
|
118
|
+
def self.create_id=(new_value)
|
119
|
+
Thread.current[CREATE_ID_TLS_KEY] = new_value.dup.freeze
|
120
|
+
end
|
93
121
|
|
94
|
-
|
95
|
-
|
96
|
-
|
122
|
+
# Returns the current create identifier.
|
123
|
+
# See also JSON.create_id=.
|
124
|
+
def self.create_id
|
125
|
+
Thread.current[CREATE_ID_TLS_KEY] || DEFAULT_CREATE_ID
|
97
126
|
end
|
98
|
-
self.create_id = 'json_class'
|
99
127
|
|
100
128
|
NaN = 0.0/0
|
101
129
|
|
@@ -126,7 +154,7 @@ module JSON
|
|
126
154
|
# This exception is raised if a generator or unparser error occurs.
|
127
155
|
class GeneratorError < JSONError; end
|
128
156
|
# For backwards compatibility
|
129
|
-
UnparserError = GeneratorError
|
157
|
+
UnparserError = GeneratorError # :nodoc:
|
130
158
|
|
131
159
|
# This exception is raised if the required unicode support is missing on the
|
132
160
|
# system. Usually this means that the iconv library is not installed.
|
@@ -134,82 +162,140 @@ module JSON
|
|
134
162
|
|
135
163
|
module_function
|
136
164
|
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
165
|
+
# :call-seq:
|
166
|
+
# JSON.parse(source, opts) -> object
|
167
|
+
#
|
168
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
169
|
+
#
|
170
|
+
# Argument +source+ contains the \String to be parsed.
|
171
|
+
#
|
172
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
173
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
174
|
+
#
|
175
|
+
# ---
|
176
|
+
#
|
177
|
+
# When +source+ is a \JSON array, returns a Ruby \Array:
|
178
|
+
# source = '["foo", 1.0, true, false, null]'
|
179
|
+
# ruby = JSON.parse(source)
|
180
|
+
# ruby # => ["foo", 1.0, true, false, nil]
|
181
|
+
# ruby.class # => Array
|
182
|
+
#
|
183
|
+
# When +source+ is a \JSON object, returns a Ruby \Hash:
|
184
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
185
|
+
# ruby = JSON.parse(source)
|
186
|
+
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
187
|
+
# ruby.class # => Hash
|
188
|
+
#
|
189
|
+
# For examples of parsing for all \JSON data types, see
|
190
|
+
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
191
|
+
#
|
192
|
+
# Parses nested JSON objects:
|
193
|
+
# source = <<-EOT
|
194
|
+
# {
|
195
|
+
# "name": "Dave",
|
196
|
+
# "age" :40,
|
197
|
+
# "hats": [
|
198
|
+
# "Cattleman's",
|
199
|
+
# "Panama",
|
200
|
+
# "Tophat"
|
201
|
+
# ]
|
202
|
+
# }
|
203
|
+
# EOT
|
204
|
+
# ruby = JSON.parse(source)
|
205
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
206
|
+
#
|
207
|
+
# ---
|
208
|
+
#
|
209
|
+
# Raises an exception if +source+ is not valid JSON:
|
210
|
+
# # Raises JSON::ParserError (783: unexpected token at ''):
|
211
|
+
# JSON.parse('')
|
212
|
+
#
|
155
213
|
def parse(source, opts = {})
|
156
|
-
Parser.new(source, opts).parse
|
214
|
+
Parser.new(source, **(opts||{})).parse
|
157
215
|
end
|
158
216
|
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
# to true.
|
171
|
-
# * *create_additions*: If set to false, the Parser doesn't create
|
172
|
-
# additions even if a matching class and create_id was found. This option
|
173
|
-
# defaults to false.
|
217
|
+
# :call-seq:
|
218
|
+
# JSON.parse!(source, opts) -> object
|
219
|
+
#
|
220
|
+
# Calls
|
221
|
+
# parse(source, opts)
|
222
|
+
# with +source+ and possibly modified +opts+.
|
223
|
+
#
|
224
|
+
# Differences from JSON.parse:
|
225
|
+
# - Option +max_nesting+, if not provided, defaults to +false+,
|
226
|
+
# which disables checking for nesting depth.
|
227
|
+
# - Option +allow_nan+, if not provided, defaults to +true+.
|
174
228
|
def parse!(source, opts = {})
|
175
229
|
opts = {
|
176
230
|
:max_nesting => false,
|
177
231
|
:allow_nan => true
|
178
232
|
}.merge(opts)
|
179
|
-
Parser.new(source, opts).parse
|
233
|
+
Parser.new(source, **(opts||{})).parse
|
234
|
+
end
|
235
|
+
|
236
|
+
# :call-seq:
|
237
|
+
# JSON.load_file(path, opts={}) -> object
|
238
|
+
#
|
239
|
+
# Calls:
|
240
|
+
# parse(File.read(path), opts)
|
241
|
+
#
|
242
|
+
# See method #parse.
|
243
|
+
def load_file(filespec, opts = {})
|
244
|
+
parse(File.read(filespec), opts)
|
180
245
|
end
|
181
246
|
|
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
|
-
#
|
207
|
-
#
|
247
|
+
# :call-seq:
|
248
|
+
# JSON.load_file!(path, opts = {})
|
249
|
+
#
|
250
|
+
# Calls:
|
251
|
+
# JSON.parse!(File.read(path, opts))
|
252
|
+
#
|
253
|
+
# See method #parse!
|
254
|
+
def load_file!(filespec, opts = {})
|
255
|
+
parse!(File.read(filespec), opts)
|
256
|
+
end
|
257
|
+
|
258
|
+
# :call-seq:
|
259
|
+
# JSON.generate(obj, opts = nil) -> new_string
|
260
|
+
#
|
261
|
+
# Returns a \String containing the generated \JSON data.
|
262
|
+
#
|
263
|
+
# See also JSON.fast_generate, JSON.pretty_generate.
|
264
|
+
#
|
265
|
+
# Argument +obj+ is the Ruby object to be converted to \JSON.
|
266
|
+
#
|
267
|
+
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
268
|
+
# See {Generating Options}[#module-JSON-label-Generating+Options].
|
269
|
+
#
|
270
|
+
# ---
|
271
|
+
#
|
272
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
273
|
+
# obj = ["foo", 1.0, true, false, nil]
|
274
|
+
# json = JSON.generate(obj)
|
275
|
+
# json # => '["foo",1.0,true,false,null]'
|
276
|
+
#
|
277
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
278
|
+
# obj = {foo: 0, bar: 's', baz: :bat}
|
279
|
+
# json = JSON.generate(obj)
|
280
|
+
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
281
|
+
#
|
282
|
+
# For examples of generating from other Ruby objects, see
|
283
|
+
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
284
|
+
#
|
285
|
+
# ---
|
286
|
+
#
|
287
|
+
# Raises an exception if any formatting option is not a \String.
|
288
|
+
#
|
289
|
+
# Raises an exception if +obj+ contains circular references:
|
290
|
+
# a = []; b = []; a.push(b); b.push(a)
|
291
|
+
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
292
|
+
# JSON.generate(a)
|
293
|
+
#
|
208
294
|
def generate(obj, opts = nil)
|
209
295
|
if State === opts
|
210
296
|
state, opts = opts, nil
|
211
297
|
else
|
212
|
-
state =
|
298
|
+
state = State.new
|
213
299
|
end
|
214
300
|
if opts
|
215
301
|
if opts.respond_to? :to_hash
|
@@ -231,16 +317,24 @@ module JSON
|
|
231
317
|
module_function :unparse
|
232
318
|
# :startdoc:
|
233
319
|
|
234
|
-
#
|
235
|
-
#
|
320
|
+
# :call-seq:
|
321
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
322
|
+
#
|
323
|
+
# Arguments +obj+ and +opts+ here are the same as
|
324
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
325
|
+
#
|
326
|
+
# By default, generates \JSON data without checking
|
327
|
+
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
236
328
|
#
|
237
|
-
#
|
238
|
-
#
|
329
|
+
# Raises an exception if +obj+ contains circular references:
|
330
|
+
# a = []; b = []; a.push(b); b.push(a)
|
331
|
+
# # Raises SystemStackError (stack level too deep):
|
332
|
+
# JSON.fast_generate(a)
|
239
333
|
def fast_generate(obj, opts = nil)
|
240
334
|
if State === opts
|
241
335
|
state, opts = opts, nil
|
242
336
|
else
|
243
|
-
state =
|
337
|
+
state = JSON.create_fast_state
|
244
338
|
end
|
245
339
|
if opts
|
246
340
|
if opts.respond_to? :to_hash
|
@@ -261,17 +355,41 @@ module JSON
|
|
261
355
|
module_function :fast_unparse
|
262
356
|
# :startdoc:
|
263
357
|
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
358
|
+
# :call-seq:
|
359
|
+
# JSON.pretty_generate(obj, opts = nil) -> new_string
|
360
|
+
#
|
361
|
+
# Arguments +obj+ and +opts+ here are the same as
|
362
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
363
|
+
#
|
364
|
+
# Default options are:
|
365
|
+
# {
|
366
|
+
# indent: ' ', # Two spaces
|
367
|
+
# space: ' ', # One space
|
368
|
+
# array_nl: "\n", # Newline
|
369
|
+
# object_nl: "\n" # Newline
|
370
|
+
# }
|
371
|
+
#
|
372
|
+
# Example:
|
373
|
+
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
374
|
+
# json = JSON.pretty_generate(obj)
|
375
|
+
# puts json
|
376
|
+
# Output:
|
377
|
+
# {
|
378
|
+
# "foo": [
|
379
|
+
# "bar",
|
380
|
+
# "baz"
|
381
|
+
# ],
|
382
|
+
# "bat": {
|
383
|
+
# "bam": 0,
|
384
|
+
# "bad": 1
|
385
|
+
# }
|
386
|
+
# }
|
267
387
|
#
|
268
|
-
# The _opts_ argument can be used to configure the generator. See the
|
269
|
-
# generate method for a more detailed explanation.
|
270
388
|
def pretty_generate(obj, opts = nil)
|
271
389
|
if State === opts
|
272
390
|
state, opts = opts, nil
|
273
391
|
else
|
274
|
-
state =
|
392
|
+
state = JSON.create_pretty_state
|
275
393
|
end
|
276
394
|
if opts
|
277
395
|
if opts.respond_to? :to_hash
|
@@ -293,10 +411,10 @@ module JSON
|
|
293
411
|
# :startdoc:
|
294
412
|
|
295
413
|
class << self
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
#
|
414
|
+
# Sets or returns default options for the JSON.load method.
|
415
|
+
# Initially:
|
416
|
+
# opts = JSON.load_default_options
|
417
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
300
418
|
attr_accessor :load_default_options
|
301
419
|
end
|
302
420
|
self.load_default_options = {
|
@@ -306,20 +424,134 @@ module JSON
|
|
306
424
|
:create_additions => true,
|
307
425
|
}
|
308
426
|
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
#
|
427
|
+
# :call-seq:
|
428
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
429
|
+
#
|
430
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
431
|
+
#
|
432
|
+
# - Argument +source+ must be, or be convertible to, a \String:
|
433
|
+
# - If +source+ responds to instance method +to_str+,
|
434
|
+
# <tt>source.to_str</tt> becomes the source.
|
435
|
+
# - If +source+ responds to instance method +to_io+,
|
436
|
+
# <tt>source.to_io.read</tt> becomes the source.
|
437
|
+
# - If +source+ responds to instance method +read+,
|
438
|
+
# <tt>source.read</tt> becomes the source.
|
439
|
+
# - If both of the following are true, source becomes the \String <tt>'null'</tt>:
|
440
|
+
# - Option +allow_blank+ specifies a truthy value.
|
441
|
+
# - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
|
442
|
+
# - Otherwise, +source+ remains the source.
|
443
|
+
# - Argument +proc+, if given, must be a \Proc that accepts one argument.
|
444
|
+
# It will be called recursively with each result (depth-first order).
|
445
|
+
# See details below.
|
446
|
+
# BEWARE: This method is meant to serialise data from trusted user input,
|
447
|
+
# like from your own database server or clients under your control, it could
|
448
|
+
# be dangerous to allow untrusted users to pass JSON sources into it.
|
449
|
+
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
|
450
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
451
|
+
# The default options can be changed via method JSON.load_default_options=.
|
452
|
+
#
|
453
|
+
# ---
|
454
|
+
#
|
455
|
+
# When no +proc+ is given, modifies +source+ as above and returns the result of
|
456
|
+
# <tt>parse(source, opts)</tt>; see #parse.
|
457
|
+
#
|
458
|
+
# Source for following examples:
|
459
|
+
# source = <<-EOT
|
460
|
+
# {
|
461
|
+
# "name": "Dave",
|
462
|
+
# "age" :40,
|
463
|
+
# "hats": [
|
464
|
+
# "Cattleman's",
|
465
|
+
# "Panama",
|
466
|
+
# "Tophat"
|
467
|
+
# ]
|
468
|
+
# }
|
469
|
+
# EOT
|
314
470
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
471
|
+
# Load a \String:
|
472
|
+
# ruby = JSON.load(source)
|
473
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
474
|
+
#
|
475
|
+
# Load an \IO object:
|
476
|
+
# require 'stringio'
|
477
|
+
# object = JSON.load(StringIO.new(source))
|
478
|
+
# object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
479
|
+
#
|
480
|
+
# Load a \File object:
|
481
|
+
# path = 't.json'
|
482
|
+
# File.write(path, source)
|
483
|
+
# File.open(path) do |file|
|
484
|
+
# JSON.load(file)
|
485
|
+
# end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
486
|
+
#
|
487
|
+
# ---
|
488
|
+
#
|
489
|
+
# When +proc+ is given:
|
490
|
+
# - Modifies +source+ as above.
|
491
|
+
# - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
|
492
|
+
# - Recursively calls <tt>proc(result)</tt>.
|
493
|
+
# - Returns the final result.
|
494
|
+
#
|
495
|
+
# Example:
|
496
|
+
# require 'json'
|
497
|
+
#
|
498
|
+
# # Some classes for the example.
|
499
|
+
# class Base
|
500
|
+
# def initialize(attributes)
|
501
|
+
# @attributes = attributes
|
502
|
+
# end
|
503
|
+
# end
|
504
|
+
# class User < Base; end
|
505
|
+
# class Account < Base; end
|
506
|
+
# class Admin < Base; end
|
507
|
+
# # The JSON source.
|
508
|
+
# json = <<-EOF
|
509
|
+
# {
|
510
|
+
# "users": [
|
511
|
+
# {"type": "User", "username": "jane", "email": "jane@example.com"},
|
512
|
+
# {"type": "User", "username": "john", "email": "john@example.com"}
|
513
|
+
# ],
|
514
|
+
# "accounts": [
|
515
|
+
# {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
|
516
|
+
# {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
|
517
|
+
# ],
|
518
|
+
# "admins": {"type": "Admin", "password": "0wn3d"}
|
519
|
+
# }
|
520
|
+
# EOF
|
521
|
+
# # Deserializer method.
|
522
|
+
# def deserialize_obj(obj, safe_types = %w(User Account Admin))
|
523
|
+
# type = obj.is_a?(Hash) && obj["type"]
|
524
|
+
# safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
|
525
|
+
# end
|
526
|
+
# # Call to JSON.load
|
527
|
+
# ruby = JSON.load(json, proc {|obj|
|
528
|
+
# case obj
|
529
|
+
# when Hash
|
530
|
+
# obj.each {|k, v| obj[k] = deserialize_obj v }
|
531
|
+
# when Array
|
532
|
+
# obj.map! {|v| deserialize_obj v }
|
533
|
+
# end
|
534
|
+
# })
|
535
|
+
# pp ruby
|
536
|
+
# Output:
|
537
|
+
# {"users"=>
|
538
|
+
# [#<User:0x00000000064c4c98
|
539
|
+
# @attributes=
|
540
|
+
# {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
|
541
|
+
# #<User:0x00000000064c4bd0
|
542
|
+
# @attributes=
|
543
|
+
# {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
|
544
|
+
# "accounts"=>
|
545
|
+
# [{"account"=>
|
546
|
+
# #<Account:0x00000000064c4928
|
547
|
+
# @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
|
548
|
+
# {"account"=>
|
549
|
+
# #<Account:0x00000000064c4680
|
550
|
+
# @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
|
551
|
+
# "admins"=>
|
552
|
+
# #<Admin:0x00000000064c41f8
|
553
|
+
# @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
|
320
554
|
#
|
321
|
-
# This method is part of the implementation of the load/dump interface of
|
322
|
-
# Marshal and YAML.
|
323
555
|
def load(source, proc = nil, options = {})
|
324
556
|
opts = load_default_options.merge options
|
325
557
|
if source.respond_to? :to_str
|
@@ -338,7 +570,7 @@ module JSON
|
|
338
570
|
end
|
339
571
|
|
340
572
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
341
|
-
def recurse_proc(result, &proc)
|
573
|
+
def recurse_proc(result, &proc) # :nodoc:
|
342
574
|
case result
|
343
575
|
when Array
|
344
576
|
result.each { |x| recurse_proc x, &proc }
|
@@ -355,32 +587,45 @@ module JSON
|
|
355
587
|
module_function :restore
|
356
588
|
|
357
589
|
class << self
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
590
|
+
# Sets or returns the default options for the JSON.dump method.
|
591
|
+
# Initially:
|
592
|
+
# opts = JSON.dump_default_options
|
593
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
362
594
|
attr_accessor :dump_default_options
|
363
595
|
end
|
364
596
|
self.dump_default_options = {
|
365
597
|
:max_nesting => false,
|
366
598
|
:allow_nan => true,
|
599
|
+
:escape_slash => false,
|
367
600
|
}
|
368
601
|
|
369
|
-
#
|
370
|
-
#
|
602
|
+
# :call-seq:
|
603
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
604
|
+
#
|
605
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
606
|
+
#
|
607
|
+
# The default options can be changed via method JSON.dump_default_options.
|
371
608
|
#
|
372
|
-
#
|
373
|
-
#
|
609
|
+
# - Argument +io+, if given, should respond to method +write+;
|
610
|
+
# the \JSON \String is written to +io+, and +io+ is returned.
|
611
|
+
# If +io+ is not given, the \JSON \String is returned.
|
612
|
+
# - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
|
374
613
|
#
|
375
|
-
#
|
376
|
-
# exception is raised. This argument is similar (but not exactly the
|
377
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
614
|
+
# ---
|
378
615
|
#
|
379
|
-
#
|
380
|
-
#
|
616
|
+
# When argument +io+ is not given, returns the \JSON \String generated from +obj+:
|
617
|
+
# obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
|
618
|
+
# json = JSON.dump(obj)
|
619
|
+
# json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
|
381
620
|
#
|
382
|
-
#
|
383
|
-
#
|
621
|
+
# When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
|
622
|
+
# path = 't.json'
|
623
|
+
# File.open(path, 'w') do |file|
|
624
|
+
# JSON.dump(obj, file)
|
625
|
+
# end # => #<File:t.json (closed)>
|
626
|
+
# puts File.read(path)
|
627
|
+
# Output:
|
628
|
+
# {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
|
384
629
|
def dump(obj, anIO = nil, limit = nil)
|
385
630
|
if anIO and limit.nil?
|
386
631
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
@@ -402,7 +647,7 @@ module JSON
|
|
402
647
|
raise ArgumentError, "exceed depth limit"
|
403
648
|
end
|
404
649
|
|
405
|
-
# Encodes string using
|
650
|
+
# Encodes string using String.encode.
|
406
651
|
def self.iconv(to, from, string)
|
407
652
|
string.encode(to, from)
|
408
653
|
end
|