json 2.1.0 → 2.6.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/CHANGES.md +63 -5
- data/LICENSE +56 -0
- data/README.md +56 -23
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +223 -58
- data/ext/json/ext/generator/generator.h +5 -2
- data/ext/json/ext/parser/extconf.rb +26 -0
- data/ext/json/ext/parser/parser.c +2973 -1744
- data/ext/json/ext/parser/parser.h +6 -1
- data/ext/json/ext/parser/parser.rl +130 -22
- data/ext/json/extconf.rb +1 -0
- data/json.gemspec +0 -0
- data/lib/json/add/bigdecimal.rb +2 -2
- data/lib/json/add/complex.rb +2 -3
- data/lib/json/add/ostruct.rb +1 -1
- data/lib/json/add/rational.rb +2 -3
- data/lib/json/add/regexp.rb +2 -2
- data/lib/json/add/set.rb +29 -0
- data/lib/json/common.rb +372 -125
- data/lib/json/pure/generator.rb +31 -10
- data/lib/json/pure/parser.rb +32 -6
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +549 -29
- metadata +19 -113
- data/.gitignore +0 -17
- data/.travis.yml +0 -19
- data/Gemfile +0 -16
- data/README-json-jruby.md +0 -33
- data/Rakefile +0 -408
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- 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/lib/json/ext/.keep +0 -0
- data/references/rfc7159.txt +0 -899
- 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/obsolete_fail1.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/json_addition_test.rb +0 -193
- data/tests/json_common_interface_test.rb +0 -126
- data/tests/json_encoding_test.rb +0 -107
- data/tests/json_ext_parser_test.rb +0 -15
- data/tests/json_fixtures_test.rb +0 -32
- data/tests/json_generator_test.rb +0 -377
- data/tests/json_generic_object_test.rb +0 -82
- data/tests/json_parser_test.rb +0 -471
- data/tests/json_string_matching_test.rb +0 -38
- data/tests/test_helper.rb +0 -21
- data/tools/diff.sh +0 -18
- data/tools/fuzz.rb +0 -131
- data/tools/server.rb +0 -62
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,61 @@ 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
|
-
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(
|
70
83
|
:indent => '',
|
71
84
|
:space => '',
|
72
85
|
:object_nl => "",
|
73
86
|
:array_nl => "",
|
74
87
|
:max_nesting => false
|
75
88
|
)
|
76
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_pretty_state
|
92
|
+
State.new(
|
77
93
|
:indent => ' ',
|
78
94
|
:space => ' ',
|
79
95
|
:object_nl => "\n",
|
80
96
|
:array_nl => "\n"
|
81
97
|
)
|
82
|
-
ensure
|
83
|
-
$VERBOSE = old
|
84
98
|
end
|
85
99
|
|
86
100
|
# Returns the JSON generator module that is used by JSON. This is
|
87
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator
|
101
|
+
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
102
|
+
# JSON.generator # => JSON::Ext::Generator
|
88
103
|
attr_reader :generator
|
89
104
|
|
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
|
105
|
+
# Sets or Returns the JSON generator state class that is used by JSON. This is
|
106
|
+
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State:
|
107
|
+
# JSON.state # => JSON::Ext::Generator::State
|
92
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
|
93
123
|
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
97
128
|
end
|
98
|
-
self.create_id = 'json_class'
|
99
129
|
|
100
130
|
NaN = 0.0/0
|
101
131
|
|
@@ -126,7 +156,7 @@ module JSON
|
|
126
156
|
# This exception is raised if a generator or unparser error occurs.
|
127
157
|
class GeneratorError < JSONError; end
|
128
158
|
# For backwards compatibility
|
129
|
-
UnparserError = GeneratorError
|
159
|
+
UnparserError = GeneratorError # :nodoc:
|
130
160
|
|
131
161
|
# This exception is raised if the required unicode support is missing on the
|
132
162
|
# system. Usually this means that the iconv library is not installed.
|
@@ -134,82 +164,140 @@ module JSON
|
|
134
164
|
|
135
165
|
module_function
|
136
166
|
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
167
|
+
# :call-seq:
|
168
|
+
# JSON.parse(source, opts) -> object
|
169
|
+
#
|
170
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
171
|
+
#
|
172
|
+
# Argument +source+ contains the \String to be parsed.
|
173
|
+
#
|
174
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
175
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
176
|
+
#
|
177
|
+
# ---
|
178
|
+
#
|
179
|
+
# When +source+ is a \JSON array, returns a Ruby \Array:
|
180
|
+
# source = '["foo", 1.0, true, false, null]'
|
181
|
+
# ruby = JSON.parse(source)
|
182
|
+
# ruby # => ["foo", 1.0, true, false, nil]
|
183
|
+
# ruby.class # => Array
|
184
|
+
#
|
185
|
+
# When +source+ is a \JSON object, returns a Ruby \Hash:
|
186
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
187
|
+
# ruby = JSON.parse(source)
|
188
|
+
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
189
|
+
# ruby.class # => Hash
|
190
|
+
#
|
191
|
+
# For examples of parsing for all \JSON data types, see
|
192
|
+
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
193
|
+
#
|
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
|
206
|
+
# ruby = JSON.parse(source)
|
207
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
208
|
+
#
|
209
|
+
# ---
|
210
|
+
#
|
211
|
+
# Raises an exception if +source+ is not valid JSON:
|
212
|
+
# # Raises JSON::ParserError (783: unexpected token at ''):
|
213
|
+
# JSON.parse('')
|
214
|
+
#
|
155
215
|
def parse(source, opts = {})
|
156
|
-
Parser.new(source, opts).parse
|
216
|
+
Parser.new(source, **(opts||{})).parse
|
157
217
|
end
|
158
218
|
|
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.
|
219
|
+
# :call-seq:
|
220
|
+
# JSON.parse!(source, opts) -> object
|
221
|
+
#
|
222
|
+
# Calls
|
223
|
+
# parse(source, opts)
|
224
|
+
# with +source+ and possibly modified +opts+.
|
225
|
+
#
|
226
|
+
# Differences from JSON.parse:
|
227
|
+
# - Option +max_nesting+, if not provided, defaults to +false+,
|
228
|
+
# which disables checking for nesting depth.
|
229
|
+
# - Option +allow_nan+, if not provided, defaults to +true+.
|
174
230
|
def parse!(source, opts = {})
|
175
231
|
opts = {
|
176
232
|
:max_nesting => false,
|
177
233
|
:allow_nan => true
|
178
234
|
}.merge(opts)
|
179
|
-
Parser.new(source, opts).parse
|
235
|
+
Parser.new(source, **(opts||{})).parse
|
236
|
+
end
|
237
|
+
|
238
|
+
# :call-seq:
|
239
|
+
# JSON.load_file(path, opts={}) -> object
|
240
|
+
#
|
241
|
+
# Calls:
|
242
|
+
# parse(File.read(path), opts)
|
243
|
+
#
|
244
|
+
# See method #parse.
|
245
|
+
def load_file(filespec, opts = {})
|
246
|
+
parse(File.read(filespec), opts)
|
180
247
|
end
|
181
248
|
|
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
|
-
#
|
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
|
262
|
+
#
|
263
|
+
# Returns a \String containing the generated \JSON data.
|
264
|
+
#
|
265
|
+
# See also JSON.fast_generate, JSON.pretty_generate.
|
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
|
+
#
|
272
|
+
# ---
|
273
|
+
#
|
274
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
275
|
+
# obj = ["foo", 1.0, true, false, nil]
|
276
|
+
# json = JSON.generate(obj)
|
277
|
+
# json # => '["foo",1.0,true,false,null]'
|
278
|
+
#
|
279
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
280
|
+
# obj = {foo: 0, bar: 's', baz: :bat}
|
281
|
+
# json = JSON.generate(obj)
|
282
|
+
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
283
|
+
#
|
284
|
+
# For examples of generating from other Ruby objects, see
|
285
|
+
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
286
|
+
#
|
287
|
+
# ---
|
288
|
+
#
|
289
|
+
# Raises an exception if any formatting option is not a \String.
|
290
|
+
#
|
291
|
+
# Raises an exception if +obj+ contains circular references:
|
292
|
+
# a = []; b = []; a.push(b); b.push(a)
|
293
|
+
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
294
|
+
# JSON.generate(a)
|
295
|
+
#
|
208
296
|
def generate(obj, opts = nil)
|
209
297
|
if State === opts
|
210
298
|
state, opts = opts, nil
|
211
299
|
else
|
212
|
-
state =
|
300
|
+
state = State.new
|
213
301
|
end
|
214
302
|
if opts
|
215
303
|
if opts.respond_to? :to_hash
|
@@ -231,16 +319,24 @@ module JSON
|
|
231
319
|
module_function :unparse
|
232
320
|
# :startdoc:
|
233
321
|
|
234
|
-
#
|
235
|
-
#
|
322
|
+
# :call-seq:
|
323
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
324
|
+
#
|
325
|
+
# Arguments +obj+ and +opts+ here are the same as
|
326
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
327
|
+
#
|
328
|
+
# By default, generates \JSON data without checking
|
329
|
+
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
236
330
|
#
|
237
|
-
#
|
238
|
-
#
|
331
|
+
# Raises an exception if +obj+ contains circular references:
|
332
|
+
# a = []; b = []; a.push(b); b.push(a)
|
333
|
+
# # Raises SystemStackError (stack level too deep):
|
334
|
+
# JSON.fast_generate(a)
|
239
335
|
def fast_generate(obj, opts = nil)
|
240
336
|
if State === opts
|
241
337
|
state, opts = opts, nil
|
242
338
|
else
|
243
|
-
state =
|
339
|
+
state = JSON.create_fast_state
|
244
340
|
end
|
245
341
|
if opts
|
246
342
|
if opts.respond_to? :to_hash
|
@@ -261,17 +357,41 @@ module JSON
|
|
261
357
|
module_function :fast_unparse
|
262
358
|
# :startdoc:
|
263
359
|
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
360
|
+
# :call-seq:
|
361
|
+
# JSON.pretty_generate(obj, opts = nil) -> new_string
|
362
|
+
#
|
363
|
+
# Arguments +obj+ and +opts+ here are the same as
|
364
|
+
# arguments +obj+ and +opts+ in JSON.generate.
|
365
|
+
#
|
366
|
+
# Default options are:
|
367
|
+
# {
|
368
|
+
# indent: ' ', # Two spaces
|
369
|
+
# space: ' ', # One space
|
370
|
+
# array_nl: "\n", # Newline
|
371
|
+
# object_nl: "\n" # Newline
|
372
|
+
# }
|
373
|
+
#
|
374
|
+
# Example:
|
375
|
+
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
376
|
+
# json = JSON.pretty_generate(obj)
|
377
|
+
# puts json
|
378
|
+
# Output:
|
379
|
+
# {
|
380
|
+
# "foo": [
|
381
|
+
# "bar",
|
382
|
+
# "baz"
|
383
|
+
# ],
|
384
|
+
# "bat": {
|
385
|
+
# "bam": 0,
|
386
|
+
# "bad": 1
|
387
|
+
# }
|
388
|
+
# }
|
267
389
|
#
|
268
|
-
# The _opts_ argument can be used to configure the generator. See the
|
269
|
-
# generate method for a more detailed explanation.
|
270
390
|
def pretty_generate(obj, opts = nil)
|
271
391
|
if State === opts
|
272
392
|
state, opts = opts, nil
|
273
393
|
else
|
274
|
-
state =
|
394
|
+
state = JSON.create_pretty_state
|
275
395
|
end
|
276
396
|
if opts
|
277
397
|
if opts.respond_to? :to_hash
|
@@ -293,10 +413,10 @@ module JSON
|
|
293
413
|
# :startdoc:
|
294
414
|
|
295
415
|
class << self
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
#
|
416
|
+
# Sets or returns default options for the JSON.load method.
|
417
|
+
# Initially:
|
418
|
+
# opts = JSON.load_default_options
|
419
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
|
300
420
|
attr_accessor :load_default_options
|
301
421
|
end
|
302
422
|
self.load_default_options = {
|
@@ -306,20 +426,134 @@ module JSON
|
|
306
426
|
:create_additions => true,
|
307
427
|
}
|
308
428
|
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
#
|
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
|
314
472
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
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"]}
|
488
|
+
#
|
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"}>}
|
320
556
|
#
|
321
|
-
# This method is part of the implementation of the load/dump interface of
|
322
|
-
# Marshal and YAML.
|
323
557
|
def load(source, proc = nil, options = {})
|
324
558
|
opts = load_default_options.merge options
|
325
559
|
if source.respond_to? :to_str
|
@@ -338,7 +572,7 @@ module JSON
|
|
338
572
|
end
|
339
573
|
|
340
574
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
341
|
-
def recurse_proc(result, &proc)
|
575
|
+
def recurse_proc(result, &proc) # :nodoc:
|
342
576
|
case result
|
343
577
|
when Array
|
344
578
|
result.each { |x| recurse_proc x, &proc }
|
@@ -355,32 +589,45 @@ module JSON
|
|
355
589
|
module_function :restore
|
356
590
|
|
357
591
|
class << self
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
592
|
+
# Sets or returns the default options for the JSON.dump method.
|
593
|
+
# Initially:
|
594
|
+
# opts = JSON.dump_default_options
|
595
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
362
596
|
attr_accessor :dump_default_options
|
363
597
|
end
|
364
598
|
self.dump_default_options = {
|
365
599
|
:max_nesting => false,
|
366
600
|
:allow_nan => true,
|
601
|
+
:escape_slash => false,
|
367
602
|
}
|
368
603
|
|
369
|
-
#
|
370
|
-
#
|
604
|
+
# :call-seq:
|
605
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
606
|
+
#
|
607
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
608
|
+
#
|
609
|
+
# The default options can be changed via method JSON.dump_default_options.
|
371
610
|
#
|
372
|
-
#
|
373
|
-
#
|
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+.
|
374
615
|
#
|
375
|
-
#
|
376
|
-
# exception is raised. This argument is similar (but not exactly the
|
377
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
616
|
+
# ---
|
378
617
|
#
|
379
|
-
#
|
380
|
-
#
|
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\"}"
|
381
622
|
#
|
382
|
-
#
|
383
|
-
#
|
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"}
|
384
631
|
def dump(obj, anIO = nil, limit = nil)
|
385
632
|
if anIO and limit.nil?
|
386
633
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
@@ -402,7 +649,7 @@ module JSON
|
|
402
649
|
raise ArgumentError, "exceed depth limit"
|
403
650
|
end
|
404
651
|
|
405
|
-
# Encodes string using
|
652
|
+
# Encodes string using String.encode.
|
406
653
|
def self.iconv(to, from, string)
|
407
654
|
string.encode(to, from)
|
408
655
|
end
|