json 2.3.0 → 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 +4 -4
- data/.travis.yml +4 -5
- data/CHANGES.md +33 -0
- data/README.md +16 -0
- data/Rakefile +8 -87
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +119 -6
- 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 +99 -71
- data/ext/json/ext/parser/parser.h +1 -0
- data/ext/json/ext/parser/parser.rl +29 -1
- data/ext/json/extconf.rb +1 -0
- data/java/src/json/ext/Generator.java +11 -30
- data/java/src/json/ext/GeneratorState.java +30 -0
- data/java/src/json/ext/Parser.java +85 -73
- data/java/src/json/ext/Parser.rl +14 -2
- data/java/src/json/ext/StringEncoder.java +8 -2
- data/json-java.gemspec +22 -21
- data/json.gemspec +0 -0
- data/json_pure.gemspec +8 -13
- data/lib/json/add/complex.rb +0 -1
- data/lib/json/add/rational.rb +0 -1
- data/lib/json/common.rb +339 -113
- data/lib/json/pure/generator.rb +29 -9
- data/lib/json/pure/parser.rb +22 -4
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +549 -29
- data/tests/json_addition_test.rb +0 -4
- data/tests/json_common_interface_test.rb +43 -0
- data/tests/json_fixtures_test.rb +9 -1
- data/tests/json_generator_test.rb +13 -2
- data/tests/json_parser_test.rb +25 -0
- data/tests/test_helper.rb +3 -3
- metadata +23 -12
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.
|
@@ -84,15 +90,18 @@ module JSON
|
|
84
90
|
end
|
85
91
|
|
86
92
|
# Returns the JSON generator module that is used by JSON. This is
|
87
|
-
# either JSON::Ext::Generator or JSON::Pure::Generator
|
93
|
+
# either JSON::Ext::Generator or JSON::Pure::Generator:
|
94
|
+
# JSON.generator # => JSON::Ext::Generator
|
88
95
|
attr_reader :generator
|
89
96
|
|
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
|
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
|
92
100
|
attr_accessor :state
|
93
101
|
|
94
|
-
#
|
95
|
-
# 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'
|
96
105
|
attr_accessor :create_id
|
97
106
|
end
|
98
107
|
self.create_id = 'json_class'
|
@@ -126,7 +135,7 @@ module JSON
|
|
126
135
|
# This exception is raised if a generator or unparser error occurs.
|
127
136
|
class GeneratorError < JSONError; end
|
128
137
|
# For backwards compatibility
|
129
|
-
UnparserError = GeneratorError
|
138
|
+
UnparserError = GeneratorError # :nodoc:
|
130
139
|
|
131
140
|
# This exception is raised if the required unicode support is missing on the
|
132
141
|
# system. Usually this means that the iconv library is not installed.
|
@@ -134,43 +143,69 @@ module JSON
|
|
134
143
|
|
135
144
|
module_function
|
136
145
|
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
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
|
+
#
|
155
194
|
def parse(source, opts = {})
|
156
195
|
Parser.new(source, **(opts||{})).parse
|
157
196
|
end
|
158
197
|
|
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.
|
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+.
|
174
209
|
def parse!(source, opts = {})
|
175
210
|
opts = {
|
176
211
|
:max_nesting => false,
|
@@ -179,32 +214,64 @@ module JSON
|
|
179
214
|
Parser.new(source, **(opts||{})).parse
|
180
215
|
end
|
181
216
|
|
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
|
-
#
|
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
|
+
#
|
208
275
|
def generate(obj, opts = nil)
|
209
276
|
if State === opts
|
210
277
|
state, opts = opts, nil
|
@@ -231,11 +298,19 @@ module JSON
|
|
231
298
|
module_function :unparse
|
232
299
|
# :startdoc:
|
233
300
|
|
234
|
-
#
|
235
|
-
#
|
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.
|
306
|
+
#
|
307
|
+
# By default, generates \JSON data without checking
|
308
|
+
# for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
|
236
309
|
#
|
237
|
-
#
|
238
|
-
#
|
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)
|
239
314
|
def fast_generate(obj, opts = nil)
|
240
315
|
if State === opts
|
241
316
|
state, opts = opts, nil
|
@@ -261,12 +336,36 @@ module JSON
|
|
261
336
|
module_function :fast_unparse
|
262
337
|
# :startdoc:
|
263
338
|
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
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
|
+
# }
|
267
368
|
#
|
268
|
-
# The _opts_ argument can be used to configure the generator. See the
|
269
|
-
# generate method for a more detailed explanation.
|
270
369
|
def pretty_generate(obj, opts = nil)
|
271
370
|
if State === opts
|
272
371
|
state, opts = opts, nil
|
@@ -293,10 +392,10 @@ module JSON
|
|
293
392
|
# :startdoc:
|
294
393
|
|
295
394
|
class << self
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
#
|
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}
|
300
399
|
attr_accessor :load_default_options
|
301
400
|
end
|
302
401
|
self.load_default_options = {
|
@@ -306,20 +405,134 @@ module JSON
|
|
306
405
|
:create_additions => true,
|
307
406
|
}
|
308
407
|
|
309
|
-
#
|
310
|
-
#
|
311
|
-
# to the read method. If _proc_ was given, it will be called with any nested
|
312
|
-
# Ruby object as an argument recursively in depth first order. To modify the
|
313
|
-
# default options pass in the optional _options_ argument as well.
|
408
|
+
# :call-seq:
|
409
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
314
410
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
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"]}
|
460
|
+
#
|
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"}>}
|
320
535
|
#
|
321
|
-
# This method is part of the implementation of the load/dump interface of
|
322
|
-
# Marshal and YAML.
|
323
536
|
def load(source, proc = nil, options = {})
|
324
537
|
opts = load_default_options.merge options
|
325
538
|
if source.respond_to? :to_str
|
@@ -338,7 +551,7 @@ module JSON
|
|
338
551
|
end
|
339
552
|
|
340
553
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
341
|
-
def recurse_proc(result, &proc)
|
554
|
+
def recurse_proc(result, &proc) # :nodoc:
|
342
555
|
case result
|
343
556
|
when Array
|
344
557
|
result.each { |x| recurse_proc x, &proc }
|
@@ -355,32 +568,45 @@ module JSON
|
|
355
568
|
module_function :restore
|
356
569
|
|
357
570
|
class << self
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
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}
|
362
575
|
attr_accessor :dump_default_options
|
363
576
|
end
|
364
577
|
self.dump_default_options = {
|
365
578
|
:max_nesting => false,
|
366
579
|
:allow_nan => true,
|
580
|
+
:escape_slash => false,
|
367
581
|
}
|
368
582
|
|
369
|
-
#
|
370
|
-
#
|
583
|
+
# :call-seq:
|
584
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
585
|
+
#
|
586
|
+
# Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
|
587
|
+
#
|
588
|
+
# The default options can be changed via method JSON.dump_default_options.
|
371
589
|
#
|
372
|
-
#
|
373
|
-
#
|
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+.
|
374
594
|
#
|
375
|
-
#
|
376
|
-
# exception is raised. This argument is similar (but not exactly the
|
377
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
595
|
+
# ---
|
378
596
|
#
|
379
|
-
#
|
380
|
-
#
|
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\"}"
|
381
601
|
#
|
382
|
-
#
|
383
|
-
#
|
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,7 +628,7 @@ module JSON
|
|
402
628
|
raise ArgumentError, "exceed depth limit"
|
403
629
|
end
|
404
630
|
|
405
|
-
# Encodes string using
|
631
|
+
# Encodes string using String.encode.
|
406
632
|
def self.iconv(to, from, string)
|
407
633
|
string.encode(to, from)
|
408
634
|
end
|
data/lib/json/pure/generator.rb
CHANGED
@@ -37,20 +37,26 @@ module JSON
|
|
37
37
|
'\\' => '\\\\',
|
38
38
|
} # :nodoc:
|
39
39
|
|
40
|
+
ESCAPE_SLASH_MAP = MAP.merge(
|
41
|
+
'/' => '\\/',
|
42
|
+
)
|
43
|
+
|
40
44
|
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
|
41
45
|
# UTF16 big endian characters as \u????, and return it.
|
42
|
-
def utf8_to_json(string) # :nodoc:
|
46
|
+
def utf8_to_json(string, escape_slash = false) # :nodoc:
|
43
47
|
string = string.dup
|
44
48
|
string.force_encoding(::Encoding::ASCII_8BIT)
|
45
|
-
|
49
|
+
map = escape_slash ? ESCAPE_SLASH_MAP : MAP
|
50
|
+
string.gsub!(/[\/"\\\x0-\x1f]/) { map[$&] || $& }
|
46
51
|
string.force_encoding(::Encoding::UTF_8)
|
47
52
|
string
|
48
53
|
end
|
49
54
|
|
50
|
-
def utf8_to_json_ascii(string) # :nodoc:
|
55
|
+
def utf8_to_json_ascii(string, escape_slash = false) # :nodoc:
|
51
56
|
string = string.dup
|
52
57
|
string.force_encoding(::Encoding::ASCII_8BIT)
|
53
|
-
|
58
|
+
map = escape_slash ? ESCAPE_SLASH_MAP : MAP
|
59
|
+
string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
|
54
60
|
string.gsub!(/(
|
55
61
|
(?:
|
56
62
|
[\xc2-\xdf][\x80-\xbf] |
|
@@ -109,6 +115,7 @@ module JSON
|
|
109
115
|
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
110
116
|
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
111
117
|
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
118
|
+
# * *escape_slash*: true if forward slash (/) should be escaped (default: false)
|
112
119
|
# * *check_circular*: is deprecated now, use the :max_nesting option instead,
|
113
120
|
# * *max_nesting*: sets the maximum level of data structure nesting in
|
114
121
|
# the generated JSON, max_nesting = 0 if no maximum should be checked.
|
@@ -123,6 +130,7 @@ module JSON
|
|
123
130
|
@array_nl = ''
|
124
131
|
@allow_nan = false
|
125
132
|
@ascii_only = false
|
133
|
+
@escape_slash = false
|
126
134
|
@buffer_initial_length = 1024
|
127
135
|
configure opts
|
128
136
|
end
|
@@ -148,6 +156,10 @@ module JSON
|
|
148
156
|
# the generated JSON, max_nesting = 0 if no maximum is checked.
|
149
157
|
attr_accessor :max_nesting
|
150
158
|
|
159
|
+
# If this attribute is set to true, forward slashes will be escaped in
|
160
|
+
# all json strings.
|
161
|
+
attr_accessor :escape_slash
|
162
|
+
|
151
163
|
# :stopdoc:
|
152
164
|
attr_reader :buffer_initial_length
|
153
165
|
|
@@ -187,6 +199,11 @@ module JSON
|
|
187
199
|
@ascii_only
|
188
200
|
end
|
189
201
|
|
202
|
+
# Returns true, if forward slashes are escaped. Otherwise returns false.
|
203
|
+
def escape_slash?
|
204
|
+
@escape_slash
|
205
|
+
end
|
206
|
+
|
190
207
|
# Configure this State instance with the Hash _opts_, and return
|
191
208
|
# itself.
|
192
209
|
def configure(opts)
|
@@ -209,6 +226,7 @@ module JSON
|
|
209
226
|
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
|
210
227
|
@depth = opts[:depth] || 0
|
211
228
|
@buffer_initial_length ||= opts[:buffer_initial_length]
|
229
|
+
@escape_slash = !!opts[:escape_slash] if opts.key?(:escape_slash)
|
212
230
|
|
213
231
|
if !opts.key?(:max_nesting) # defaults to 100
|
214
232
|
@max_nesting = 100
|
@@ -314,8 +332,10 @@ module JSON
|
|
314
332
|
first = false
|
315
333
|
}
|
316
334
|
depth = state.depth -= 1
|
317
|
-
|
318
|
-
|
335
|
+
unless first
|
336
|
+
result << state.object_nl
|
337
|
+
result << state.indent * depth if indent
|
338
|
+
end
|
319
339
|
result << '}'
|
320
340
|
result
|
321
341
|
end
|
@@ -399,13 +419,13 @@ module JSON
|
|
399
419
|
string = encode(::Encoding::UTF_8)
|
400
420
|
end
|
401
421
|
if state.ascii_only?
|
402
|
-
'"' << JSON.utf8_to_json_ascii(string) << '"'
|
422
|
+
'"' << JSON.utf8_to_json_ascii(string, state.escape_slash) << '"'
|
403
423
|
else
|
404
|
-
'"' << JSON.utf8_to_json(string) << '"'
|
424
|
+
'"' << JSON.utf8_to_json(string, state.escape_slash) << '"'
|
405
425
|
end
|
406
426
|
end
|
407
427
|
|
408
|
-
# Module that holds the
|
428
|
+
# Module that holds the extending methods if, the String module is
|
409
429
|
# included.
|
410
430
|
module Extend
|
411
431
|
# Raw Strings are JSON Objects (the raw bytes are stored in an
|