json 2.3.1 → 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 +2 -5
- data/LICENSE +56 -0
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +48 -5
- 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 +71 -44
- data/ext/json/ext/parser/parser.h +1 -0
- data/ext/json/ext/parser/parser.rl +28 -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 +7 -6
- data/lib/json.rb +171 -0
- data/lib/json/add/complex.rb +0 -1
- data/lib/json/add/rational.rb +0 -1
- data/lib/json/common.rb +206 -215
- data/lib/json/pure/generator.rb +28 -8
- data/lib/json/pure/parser.rb +20 -2
- data/tests/json_addition_test.rb +0 -4
- data/tests/json_common_interface_test.rb +43 -0
- data/tests/json_fixtures_test.rb +3 -0
- data/tests/json_generator_test.rb +13 -2
- data/tests/json_parser_test.rb +25 -0
- data/tests/test_helper.rb +3 -3
- metadata +4 -3
data/java/src/json/ext/Parser.rl
CHANGED
@@ -50,6 +50,7 @@ public class Parser extends RubyObject {
|
|
50
50
|
private int maxNesting;
|
51
51
|
private boolean allowNaN;
|
52
52
|
private boolean symbolizeNames;
|
53
|
+
private boolean freeze;
|
53
54
|
private RubyClass objectClass;
|
54
55
|
private RubyClass arrayClass;
|
55
56
|
private RubyClass decimalClass;
|
@@ -158,6 +159,7 @@ public class Parser extends RubyObject {
|
|
158
159
|
this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
|
159
160
|
this.allowNaN = opts.getBool("allow_nan", false);
|
160
161
|
this.symbolizeNames = opts.getBool("symbolize_names", false);
|
162
|
+
this.freeze = opts.getBool("freeze", false);
|
161
163
|
this.createId = opts.getString("create_id", getCreateId(context));
|
162
164
|
this.createAdditions = opts.getBool("create_additions", false);
|
163
165
|
this.objectClass = opts.getClass("object_class", runtime.getHash());
|
@@ -452,6 +454,9 @@ public class Parser extends RubyObject {
|
|
452
454
|
%% write exec;
|
453
455
|
|
454
456
|
if (cs >= JSON_value_first_final && result != null) {
|
457
|
+
if (parser.freeze) {
|
458
|
+
result.setFrozen(true);
|
459
|
+
}
|
455
460
|
res.update(result, p);
|
456
461
|
} else {
|
457
462
|
res.update(null, p);
|
@@ -633,9 +638,16 @@ public class Parser extends RubyObject {
|
|
633
638
|
|
634
639
|
if (cs >= JSON_string_first_final && result != null) {
|
635
640
|
if (result instanceof RubyString) {
|
636
|
-
(
|
641
|
+
RubyString string = (RubyString)result;
|
642
|
+
string.force_encoding(context, info.utf8.get());
|
643
|
+
if (parser.freeze) {
|
644
|
+
string.setFrozen(true);
|
645
|
+
string = getRuntime().freezeAndDedupString(string);
|
646
|
+
}
|
647
|
+
res.update(string, p + 1);
|
648
|
+
} else {
|
649
|
+
res.update(result, p + 1);
|
637
650
|
}
|
638
|
-
res.update(result, p + 1);
|
639
651
|
} else {
|
640
652
|
res.update(null, p + 1);
|
641
653
|
}
|
@@ -15,7 +15,7 @@ import org.jruby.util.ByteList;
|
|
15
15
|
* and throws a GeneratorError if any problem is found.
|
16
16
|
*/
|
17
17
|
final class StringEncoder extends ByteListTranscoder {
|
18
|
-
private final boolean asciiOnly;
|
18
|
+
private final boolean asciiOnly, escapeSlash;
|
19
19
|
|
20
20
|
// Escaped characters will reuse this array, to avoid new allocations
|
21
21
|
// or appending them byte-by-byte
|
@@ -37,9 +37,10 @@ final class StringEncoder extends ByteListTranscoder {
|
|
37
37
|
new byte[] {'0', '1', '2', '3', '4', '5', '6', '7',
|
38
38
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
39
39
|
|
40
|
-
StringEncoder(ThreadContext context, boolean asciiOnly) {
|
40
|
+
StringEncoder(ThreadContext context, boolean asciiOnly, boolean escapeSlash) {
|
41
41
|
super(context);
|
42
42
|
this.asciiOnly = asciiOnly;
|
43
|
+
this.escapeSlash = escapeSlash;
|
43
44
|
}
|
44
45
|
|
45
46
|
void encode(ByteList src, ByteList out) {
|
@@ -73,6 +74,11 @@ final class StringEncoder extends ByteListTranscoder {
|
|
73
74
|
case '\b':
|
74
75
|
escapeChar('b');
|
75
76
|
break;
|
77
|
+
case '/':
|
78
|
+
if(escapeSlash) {
|
79
|
+
escapeChar((char)c);
|
80
|
+
break;
|
81
|
+
}
|
76
82
|
default:
|
77
83
|
if (c >= 0x20 && c <= 0x7f ||
|
78
84
|
(c >= 0x80 && !asciiOnly)) {
|
data/json-java.gemspec
CHANGED
@@ -1,33 +1,34 @@
|
|
1
|
-
|
2
|
-
require "rubygems"
|
1
|
+
# -*- encoding: utf-8 -*-
|
3
2
|
|
4
3
|
spec = Gem::Specification.new do |s|
|
5
4
|
s.name = "json"
|
6
5
|
s.version = File.read("VERSION").chomp
|
7
|
-
|
6
|
+
|
7
|
+
s.summary = "JSON Implementation for Ruby"
|
8
8
|
s.description = "A JSON implementation as a JRuby extension."
|
9
|
+
s.licenses = ["Ruby"]
|
9
10
|
s.author = "Daniel Luz"
|
10
11
|
s.email = "dev+ruby@mernen.com"
|
11
|
-
|
12
|
+
|
12
13
|
s.platform = 'java'
|
13
|
-
s.licenses = ["Ruby"]
|
14
14
|
|
15
|
-
s.files = Dir["{docs,lib,tests}/**/*"]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
15
|
+
s.files = Dir["{docs,lib,tests}/**/*", "LICENSE"]
|
16
|
+
|
17
|
+
s.homepage = "http://flori.github.com/json"
|
18
|
+
s.metadata = {
|
19
|
+
'bug_tracker_uri' => 'https://github.com/flori/json/issues',
|
20
|
+
'changelog_uri' => 'https://github.com/flori/json/blob/master/CHANGES.md',
|
21
|
+
'documentation_uri' => 'http://flori.github.io/json/doc/index.html',
|
22
|
+
'homepage_uri' => 'http://flori.github.io/json/',
|
23
|
+
'source_code_uri' => 'https://github.com/flori/json',
|
24
|
+
'wiki_uri' => 'https://github.com/flori/json/wiki'
|
25
|
+
}
|
26
|
+
|
27
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.0")
|
28
|
+
s.test_files = ["tests/test_helper.rb"]
|
29
|
+
|
30
|
+
s.add_development_dependency("rake", [">= 0"])
|
31
|
+
s.add_development_dependency("test-unit", [">= 2.0", "< 4.0"])
|
31
32
|
end
|
32
33
|
|
33
34
|
if $0 == __FILE__
|
data/json.gemspec
CHANGED
@@ -4,18 +4,21 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.name = "json"
|
5
5
|
s.version = File.read('VERSION').chomp
|
6
6
|
|
7
|
-
s.
|
8
|
-
s.require_paths = ["lib"]
|
9
|
-
s.authors = ["Florian Frank"]
|
7
|
+
s.summary = "JSON Implementation for Ruby"
|
10
8
|
s.description = "This is a JSON implementation as a Ruby extension in C."
|
9
|
+
s.licenses = ["Ruby"]
|
10
|
+
s.authors = ["Florian Frank"]
|
11
11
|
s.email = "flori@ping.de"
|
12
|
+
|
12
13
|
s.extensions = ["ext/json/ext/generator/extconf.rb", "ext/json/ext/parser/extconf.rb", "ext/json/extconf.rb"]
|
13
14
|
s.extra_rdoc_files = ["README.md"]
|
15
|
+
s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"]
|
14
16
|
s.files = [
|
15
17
|
".gitignore",
|
16
18
|
".travis.yml",
|
17
19
|
"CHANGES.md",
|
18
20
|
"Gemfile",
|
21
|
+
"LICENSE",
|
19
22
|
"README-json-jruby.md",
|
20
23
|
"README.md",
|
21
24
|
"Rakefile",
|
@@ -128,10 +131,8 @@ Gem::Specification.new do |s|
|
|
128
131
|
'source_code_uri' => 'https://github.com/flori/json',
|
129
132
|
'wiki_uri' => 'https://github.com/flori/json/wiki'
|
130
133
|
}
|
131
|
-
|
132
|
-
s.rdoc_options = ["--title", "JSON implemention for Ruby", "--main", "README.md"]
|
134
|
+
|
133
135
|
s.required_ruby_version = Gem::Requirement.new(">= 2.0")
|
134
|
-
s.summary = "JSON Implementation for Ruby"
|
135
136
|
s.test_files = ["tests/test_helper.rb"]
|
136
137
|
|
137
138
|
s.add_development_dependency("rake", [">= 0"])
|
data/lib/json.rb
CHANGED
@@ -107,6 +107,89 @@ require 'json/common'
|
|
107
107
|
# ruby # => nil
|
108
108
|
# ruby.class # => NilClass
|
109
109
|
#
|
110
|
+
# ==== Parsing Options
|
111
|
+
#
|
112
|
+
# ====== Input Options
|
113
|
+
#
|
114
|
+
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
|
115
|
+
# defaults to +100+; specify +false+ to disable depth checking.
|
116
|
+
#
|
117
|
+
# With the default, +false+:
|
118
|
+
# source = '[0, [1, [2, [3]]]]'
|
119
|
+
# ruby = JSON.parse(source)
|
120
|
+
# ruby # => [0, [1, [2, [3]]]]
|
121
|
+
# Too deep:
|
122
|
+
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
123
|
+
# JSON.parse(source, {max_nesting: 1})
|
124
|
+
# Bad value:
|
125
|
+
# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
|
126
|
+
# JSON.parse(source, {max_nesting: :foo})
|
127
|
+
#
|
128
|
+
# ---
|
129
|
+
#
|
130
|
+
# Option +allow_nan+ (boolean) specifies whether to allow
|
131
|
+
# NaN, Infinity, and MinusInfinity in +source+;
|
132
|
+
# defaults to +false+.
|
133
|
+
#
|
134
|
+
# With the default, +false+:
|
135
|
+
# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
|
136
|
+
# JSON.parse('[NaN]')
|
137
|
+
# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
|
138
|
+
# JSON.parse('[Infinity]')
|
139
|
+
# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
|
140
|
+
# JSON.parse('[-Infinity]')
|
141
|
+
# Allow:
|
142
|
+
# source = '[NaN, Infinity, -Infinity]'
|
143
|
+
# ruby = JSON.parse(source, {allow_nan: true})
|
144
|
+
# ruby # => [NaN, Infinity, -Infinity]
|
145
|
+
#
|
146
|
+
# ====== Output Options
|
147
|
+
#
|
148
|
+
# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
|
149
|
+
# should be Symbols;
|
150
|
+
# defaults to +false+ (use Strings).
|
151
|
+
#
|
152
|
+
# With the default, +false+:
|
153
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
154
|
+
# ruby = JSON.parse(source)
|
155
|
+
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
156
|
+
# Use Symbols:
|
157
|
+
# ruby = JSON.parse(source, {symbolize_names: true})
|
158
|
+
# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
|
159
|
+
#
|
160
|
+
# ---
|
161
|
+
#
|
162
|
+
# Option +object_class+ (\Class) specifies the Ruby class to be used
|
163
|
+
# for each \JSON object;
|
164
|
+
# defaults to \Hash.
|
165
|
+
#
|
166
|
+
# With the default, \Hash:
|
167
|
+
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
168
|
+
# ruby = JSON.parse(source)
|
169
|
+
# ruby.class # => Hash
|
170
|
+
# Use class \OpenStruct:
|
171
|
+
# ruby = JSON.parse(source, {object_class: OpenStruct})
|
172
|
+
# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
|
173
|
+
#
|
174
|
+
# ---
|
175
|
+
#
|
176
|
+
# Option +array_class+ (\Class) specifies the Ruby class to be used
|
177
|
+
# for each \JSON array;
|
178
|
+
# defaults to \Array.
|
179
|
+
#
|
180
|
+
# With the default, \Array:
|
181
|
+
# source = '["foo", 1.0, true, false, null]'
|
182
|
+
# ruby = JSON.parse(source)
|
183
|
+
# ruby.class # => Array
|
184
|
+
# Use class \Set:
|
185
|
+
# ruby = JSON.parse(source, {array_class: Set})
|
186
|
+
# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
|
187
|
+
#
|
188
|
+
# ---
|
189
|
+
#
|
190
|
+
# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
|
191
|
+
# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
|
192
|
+
#
|
110
193
|
# === Generating \JSON
|
111
194
|
#
|
112
195
|
# To generate a Ruby \String containing \JSON data,
|
@@ -169,6 +252,94 @@ require 'json/common'
|
|
169
252
|
# JSON.generate(Complex(0, 0)) # => '"0+0i"'
|
170
253
|
# JSON.generate(Dir.new('.')) # => '"#<Dir>"'
|
171
254
|
#
|
255
|
+
# ==== Generating Options
|
256
|
+
#
|
257
|
+
# ====== Input Options
|
258
|
+
#
|
259
|
+
# Option +allow_nan+ (boolean) specifies whether
|
260
|
+
# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
|
261
|
+
# defaults to +false+.
|
262
|
+
#
|
263
|
+
# With the default, +false+:
|
264
|
+
# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
|
265
|
+
# JSON.generate(JSON::NaN)
|
266
|
+
# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
|
267
|
+
# JSON.generate(JSON::Infinity)
|
268
|
+
# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
|
269
|
+
# JSON.generate(JSON::MinusInfinity)
|
270
|
+
#
|
271
|
+
# Allow:
|
272
|
+
# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
|
273
|
+
# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
|
274
|
+
#
|
275
|
+
# ---
|
276
|
+
#
|
277
|
+
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
|
278
|
+
# in +obj+; defaults to +100+.
|
279
|
+
#
|
280
|
+
# With the default, +100+:
|
281
|
+
# obj = [[[[[[0]]]]]]
|
282
|
+
# JSON.generate(obj) # => '[[[[[[0]]]]]]'
|
283
|
+
#
|
284
|
+
# Too deep:
|
285
|
+
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
286
|
+
# JSON.generate(obj, max_nesting: 2)
|
287
|
+
#
|
288
|
+
# ====== Output Options
|
289
|
+
#
|
290
|
+
# The default formatting options generate the most compact
|
291
|
+
# \JSON data, all on one line and with no whitespace.
|
292
|
+
#
|
293
|
+
# You can use these formatting options to generate
|
294
|
+
# \JSON data in a more open format, using whitespace.
|
295
|
+
# See also JSON.pretty_generate.
|
296
|
+
#
|
297
|
+
# - Option +array_nl+ (\String) specifies a string (usually a newline)
|
298
|
+
# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
|
299
|
+
# - Option +object_nl+ (\String) specifies a string (usually a newline)
|
300
|
+
# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
|
301
|
+
# - Option +indent+ (\String) specifies the string (usually spaces) to be
|
302
|
+
# used for indentation; defaults to the empty \String, <tt>''</tt>;
|
303
|
+
# defaults to the empty \String, <tt>''</tt>;
|
304
|
+
# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
|
305
|
+
# - Option +space+ (\String) specifies a string (usually a space) to be
|
306
|
+
# inserted after the colon in each \JSON object's pair;
|
307
|
+
# defaults to the empty \String, <tt>''</tt>.
|
308
|
+
# - Option +space_before+ (\String) specifies a string (usually a space) to be
|
309
|
+
# inserted before the colon in each \JSON object's pair;
|
310
|
+
# defaults to the empty \String, <tt>''</tt>.
|
311
|
+
#
|
312
|
+
# In this example, +obj+ is used first to generate the shortest
|
313
|
+
# \JSON data (no whitespace), then again with all formatting options
|
314
|
+
# specified:
|
315
|
+
#
|
316
|
+
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
317
|
+
# json = JSON.generate(obj)
|
318
|
+
# puts 'Compact:', json
|
319
|
+
# opts = {
|
320
|
+
# array_nl: "\n",
|
321
|
+
# object_nl: "\n",
|
322
|
+
# indent: ' ',
|
323
|
+
# space_before: ' ',
|
324
|
+
# space: ' '
|
325
|
+
# }
|
326
|
+
# puts 'Open:', JSON.generate(obj, opts)
|
327
|
+
#
|
328
|
+
# Output:
|
329
|
+
# Compact:
|
330
|
+
# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
|
331
|
+
# Open:
|
332
|
+
# {
|
333
|
+
# "foo" : [
|
334
|
+
# "bar",
|
335
|
+
# "baz"
|
336
|
+
# ],
|
337
|
+
# "bat" : {
|
338
|
+
# "bam" : 0,
|
339
|
+
# "bad" : 1
|
340
|
+
# }
|
341
|
+
# }
|
342
|
+
#
|
172
343
|
# == \JSON Additions
|
173
344
|
#
|
174
345
|
# When you "round trip" a non-\String object from Ruby to \JSON and back,
|
data/lib/json/add/complex.rb
CHANGED
data/lib/json/add/rational.rb
CHANGED
data/lib/json/common.rb
CHANGED
@@ -4,13 +4,15 @@ require 'json/generic_object'
|
|
4
4
|
|
5
5
|
module JSON
|
6
6
|
class << self
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# :call-seq:
|
8
|
+
# JSON[object] -> new_array or new_string
|
9
|
+
#
|
10
|
+
# If +object+ is a \String,
|
11
|
+
# calls JSON.parse with +object+ and +opts+ (see method #parse):
|
10
12
|
# json = '[0, 1, null]'
|
11
13
|
# JSON[json]# => [0, 1, nil]
|
12
14
|
#
|
13
|
-
# Otherwise, calls JSON.generate with +object+ and +opts
|
15
|
+
# Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
|
14
16
|
# ruby = [0, 1, nil]
|
15
17
|
# JSON[ruby] # => '[0,1,null]'
|
16
18
|
def [](object, opts = {})
|
@@ -144,15 +146,12 @@ module JSON
|
|
144
146
|
# :call-seq:
|
145
147
|
# JSON.parse(source, opts) -> object
|
146
148
|
#
|
147
|
-
#
|
148
|
-
# {String-convertible object}[doc/implicit_conversion_rdoc.html#label-String-Convertible+Objects]
|
149
|
-
# (implementing +to_str+), and must contain valid \JSON data.
|
149
|
+
# Returns the Ruby objects created by parsing the given +source+.
|
150
150
|
#
|
151
|
-
# Argument +
|
152
|
-
# {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects]
|
153
|
-
# (implementing +to_hash+).
|
151
|
+
# Argument +source+ contains the \String to be parsed.
|
154
152
|
#
|
155
|
-
#
|
153
|
+
# Argument +opts+, if given, contains a \Hash of options for the parsing.
|
154
|
+
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
|
156
155
|
#
|
157
156
|
# ---
|
158
157
|
#
|
@@ -171,91 +170,24 @@ module JSON
|
|
171
170
|
# For examples of parsing for all \JSON data types, see
|
172
171
|
# {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
|
173
172
|
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
# Bad value:
|
187
|
-
# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
|
188
|
-
# JSON.parse(source, {max_nesting: :foo})
|
189
|
-
#
|
190
|
-
# ---
|
191
|
-
#
|
192
|
-
# Option +allow_nan+ (boolean) specifies whether to allow
|
193
|
-
# NaN, Infinity, and MinusInfinity in +source+;
|
194
|
-
# defaults to +false+.
|
195
|
-
#
|
196
|
-
# With the default, +false+:
|
197
|
-
# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
|
198
|
-
# JSON.parse('[NaN]')
|
199
|
-
# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
|
200
|
-
# JSON.parse('[Infinity]')
|
201
|
-
# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
|
202
|
-
# JSON.parse('[-Infinity]')
|
203
|
-
# Allow:
|
204
|
-
# source = '[NaN, Infinity, -Infinity]'
|
205
|
-
# ruby = JSON.parse(source, {allow_nan: true})
|
206
|
-
# ruby # => [NaN, Infinity, -Infinity]
|
207
|
-
#
|
208
|
-
# ====== Output Options
|
209
|
-
#
|
210
|
-
# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
|
211
|
-
# should be Symbols;
|
212
|
-
# defaults to +false+ (use Strings).
|
213
|
-
#
|
214
|
-
# With the default, +false+:
|
215
|
-
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
216
|
-
# ruby = JSON.parse(source)
|
217
|
-
# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
|
218
|
-
# Use Symbols:
|
219
|
-
# ruby = JSON.parse(source, {symbolize_names: true})
|
220
|
-
# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
|
221
|
-
#
|
222
|
-
# ---
|
223
|
-
#
|
224
|
-
# Option +object_class+ (\Class) specifies the Ruby class to be used
|
225
|
-
# for each \JSON object;
|
226
|
-
# defaults to \Hash.
|
227
|
-
#
|
228
|
-
# With the default, \Hash:
|
229
|
-
# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
|
230
|
-
# ruby = JSON.parse(source)
|
231
|
-
# ruby.class # => Hash
|
232
|
-
# Use class \OpenStruct:
|
233
|
-
# ruby = JSON.parse(source, {object_class: OpenStruct})
|
234
|
-
# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
|
235
|
-
#
|
236
|
-
# ---
|
237
|
-
#
|
238
|
-
# Option +array_class+ (\Class) specifies the Ruby class to be used
|
239
|
-
# for each \JSON array;
|
240
|
-
# defaults to \Array.
|
241
|
-
#
|
242
|
-
# With the default, \Array:
|
243
|
-
# source = '["foo", 1.0, true, false, null]'
|
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
|
244
185
|
# ruby = JSON.parse(source)
|
245
|
-
# ruby
|
246
|
-
# Use class \Set:
|
247
|
-
# ruby = JSON.parse(source, {array_class: Set})
|
248
|
-
# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
|
186
|
+
# ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
|
249
187
|
#
|
250
188
|
# ---
|
251
189
|
#
|
252
|
-
# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
|
253
|
-
# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
|
254
|
-
#
|
255
|
-
# ====== Exceptions
|
256
|
-
#
|
257
190
|
# Raises an exception if +source+ is not valid JSON:
|
258
|
-
#
|
259
191
|
# # Raises JSON::ParserError (783: unexpected token at ''):
|
260
192
|
# JSON.parse('')
|
261
193
|
#
|
@@ -283,30 +215,47 @@ module JSON
|
|
283
215
|
end
|
284
216
|
|
285
217
|
# :call-seq:
|
286
|
-
# JSON.
|
218
|
+
# JSON.load_file(path, opts={}) -> object
|
287
219
|
#
|
288
|
-
#
|
220
|
+
# Calls:
|
221
|
+
# parse(File.read(path), opts)
|
289
222
|
#
|
290
|
-
#
|
291
|
-
|
292
|
-
|
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
|
293
241
|
#
|
294
242
|
# Returns a \String containing the generated \JSON data.
|
295
243
|
#
|
296
244
|
# See also JSON.fast_generate, JSON.pretty_generate.
|
297
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
|
+
#
|
298
251
|
# ---
|
299
252
|
#
|
300
|
-
# When +obj+ is an
|
301
|
-
# {Array-convertible object}[doc/implicit_conversion_rdoc.html#label-Array-Convertible+Objects]
|
302
|
-
# (implementing +to_ary+), returns a \String containing a \JSON array:
|
253
|
+
# When +obj+ is an \Array, returns a \String containing a \JSON array:
|
303
254
|
# obj = ["foo", 1.0, true, false, nil]
|
304
255
|
# json = JSON.generate(obj)
|
305
256
|
# json # => '["foo",1.0,true,false,null]'
|
306
257
|
#
|
307
|
-
# When +obj+ is a
|
308
|
-
# {Hash-convertible object}[doc/implicit_conversion_rdoc.html#label-Hash-Convertible+Objects],
|
309
|
-
# return a \String containing a \JSON object:
|
258
|
+
# When +obj+ is a \Hash, returns a \String containing a \JSON object:
|
310
259
|
# obj = {foo: 0, bar: 's', baz: :bat}
|
311
260
|
# json = JSON.generate(obj)
|
312
261
|
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
@@ -314,98 +263,10 @@ module JSON
|
|
314
263
|
# For examples of generating from other Ruby objects, see
|
315
264
|
# {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
|
316
265
|
#
|
317
|
-
# ====== Input Options
|
318
|
-
#
|
319
|
-
# Option +allow_nan+ (boolean) specifies whether
|
320
|
-
# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
|
321
|
-
# defaults to +false+.
|
322
|
-
#
|
323
|
-
# With the default, +false+:
|
324
|
-
# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
|
325
|
-
# JSON.generate(JSON::NaN)
|
326
|
-
# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
|
327
|
-
# JSON.generate(JSON::Infinity)
|
328
|
-
# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
|
329
|
-
# JSON.generate(JSON::MinusInfinity)
|
330
|
-
#
|
331
|
-
# Allow:
|
332
|
-
# ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
|
333
|
-
# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
|
334
|
-
#
|
335
|
-
# ---
|
336
|
-
#
|
337
|
-
# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
|
338
|
-
# in +obj+; defaults to +100+.
|
339
|
-
#
|
340
|
-
# With the default, +100+:
|
341
|
-
# obj = [[[[[[0]]]]]]
|
342
|
-
# JSON.generate(obj) # => '[[[[[[0]]]]]]'
|
343
|
-
#
|
344
|
-
# Too deep:
|
345
|
-
# # Raises JSON::NestingError (nesting of 2 is too deep):
|
346
|
-
# JSON.generate(obj, max_nesting: 2)
|
347
|
-
#
|
348
|
-
# ====== Output Options
|
349
|
-
#
|
350
|
-
# The default formatting options generate the most compact
|
351
|
-
# \JSON data, all on one line and with no whitespace.
|
352
|
-
#
|
353
|
-
# You can use these formatting options to generate
|
354
|
-
# \JSON data in a more open format, using whitespace.
|
355
|
-
# See also JSON.pretty_generate.
|
356
|
-
#
|
357
|
-
# - Option +array_nl+ (\String) specifies a string (usually a newline)
|
358
|
-
# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
|
359
|
-
# - Option +object_nl+ (\String) specifies a string (usually a newline)
|
360
|
-
# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
|
361
|
-
# - Option +indent+ (\String) specifies the string (usually spaces) to be
|
362
|
-
# used for indentation; defaults to the empty \String, <tt>''</tt>;
|
363
|
-
# defaults to the empty \String, <tt>''</tt>;
|
364
|
-
# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
|
365
|
-
# - Option +space+ (\String) specifies a string (usually a space) to be
|
366
|
-
# inserted after the colon in each \JSON object's pair;
|
367
|
-
# defaults to the empty \String, <tt>''</tt>.
|
368
|
-
# - Option +space_before+ (\String) specifies a string (usually a space) to be
|
369
|
-
# inserted before the colon in each \JSON object's pair;
|
370
|
-
# defaults to the empty \String, <tt>''</tt>.
|
371
|
-
#
|
372
|
-
# In this example, +obj+ is used first to generate the shortest
|
373
|
-
# \JSON data (no whitespace), then again with all formatting options
|
374
|
-
# specified:
|
375
|
-
#
|
376
|
-
# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
|
377
|
-
# json = JSON.generate(obj)
|
378
|
-
# puts 'Compact:', json
|
379
|
-
# opts = {
|
380
|
-
# array_nl: "\n",
|
381
|
-
# object_nl: "\n",
|
382
|
-
# indent+: ' ',
|
383
|
-
# space_before: ' ',
|
384
|
-
# space: ' '
|
385
|
-
# }
|
386
|
-
# puts 'Open:', JSON.generate(obj, opts)
|
387
|
-
#
|
388
|
-
# Output:
|
389
|
-
# Compact:
|
390
|
-
# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
|
391
|
-
# Open:
|
392
|
-
# {
|
393
|
-
# "foo" : [
|
394
|
-
# "bar",
|
395
|
-
# "baz"
|
396
|
-
# ],
|
397
|
-
# "bat" : {
|
398
|
-
# "bam" : 0,
|
399
|
-
# "bad" : 1
|
400
|
-
# }
|
401
|
-
# }
|
402
|
-
#
|
403
266
|
# ---
|
404
267
|
#
|
405
268
|
# Raises an exception if any formatting option is not a \String.
|
406
269
|
#
|
407
|
-
# ====== Exceptions
|
408
|
-
#
|
409
270
|
# Raises an exception if +obj+ contains circular references:
|
410
271
|
# a = []; b = []; a.push(b); b.push(a)
|
411
272
|
# # Raises JSON::NestingError (nesting of 100 is too deep):
|
@@ -437,6 +298,9 @@ module JSON
|
|
437
298
|
module_function :unparse
|
438
299
|
# :startdoc:
|
439
300
|
|
301
|
+
# :call-seq:
|
302
|
+
# JSON.fast_generate(obj, opts) -> new_string
|
303
|
+
#
|
440
304
|
# Arguments +obj+ and +opts+ here are the same as
|
441
305
|
# arguments +obj+ and +opts+ in JSON.generate.
|
442
306
|
#
|
@@ -541,20 +405,134 @@ module JSON
|
|
541
405
|
:create_additions => true,
|
542
406
|
}
|
543
407
|
|
544
|
-
#
|
545
|
-
#
|
546
|
-
# to the read method. If _proc_ was given, it will be called with any nested
|
547
|
-
# Ruby object as an argument recursively in depth first order. To modify the
|
548
|
-
# default options pass in the optional _options_ argument as well.
|
408
|
+
# :call-seq:
|
409
|
+
# JSON.load(source, proc = nil, options = {}) -> object
|
549
410
|
#
|
550
|
-
#
|
551
|
-
#
|
552
|
-
#
|
553
|
-
#
|
554
|
-
#
|
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"}>}
|
555
535
|
#
|
556
|
-
# This method is part of the implementation of the load/dump interface of
|
557
|
-
# Marshal and YAML.
|
558
536
|
def load(source, proc = nil, options = {})
|
559
537
|
opts = load_default_options.merge options
|
560
538
|
if source.respond_to? :to_str
|
@@ -573,7 +551,7 @@ module JSON
|
|
573
551
|
end
|
574
552
|
|
575
553
|
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
|
576
|
-
def recurse_proc(result, &proc)
|
554
|
+
def recurse_proc(result, &proc) # :nodoc:
|
577
555
|
case result
|
578
556
|
when Array
|
579
557
|
result.each { |x| recurse_proc x, &proc }
|
@@ -593,29 +571,42 @@ module JSON
|
|
593
571
|
# Sets or returns the default options for the JSON.dump method.
|
594
572
|
# Initially:
|
595
573
|
# opts = JSON.dump_default_options
|
596
|
-
# opts # => {:max_nesting=>false, :allow_nan=>true}
|
574
|
+
# opts # => {:max_nesting=>false, :allow_nan=>true, :escape_slash=>false}
|
597
575
|
attr_accessor :dump_default_options
|
598
576
|
end
|
599
577
|
self.dump_default_options = {
|
600
578
|
:max_nesting => false,
|
601
579
|
:allow_nan => true,
|
580
|
+
:escape_slash => false,
|
602
581
|
}
|
603
582
|
|
604
|
-
#
|
605
|
-
#
|
583
|
+
# :call-seq:
|
584
|
+
# JSON.dump(obj, io = nil, limit = nil)
|
606
585
|
#
|
607
|
-
#
|
608
|
-
# 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.
|
609
587
|
#
|
610
|
-
#
|
611
|
-
# exception is raised. This argument is similar (but not exactly the
|
612
|
-
# same!) to the _limit_ argument in Marshal.dump.
|
588
|
+
# The default options can be changed via method JSON.dump_default_options.
|
613
589
|
#
|
614
|
-
#
|
615
|
-
#
|
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+.
|
616
594
|
#
|
617
|
-
#
|
618
|
-
#
|
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"}
|
619
610
|
def dump(obj, anIO = nil, limit = nil)
|
620
611
|
if anIO and limit.nil?
|
621
612
|
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|