json 2.0.3 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +11 -4
- data/CHANGES.md +44 -0
- data/Gemfile +1 -3
- data/README.md +54 -21
- data/Rakefile +19 -93
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +172 -46
- data/ext/json/ext/generator/generator.h +0 -1
- data/ext/json/ext/parser/parser.c +138 -85
- data/ext/json/ext/parser/parser.h +1 -0
- data/ext/json/ext/parser/parser.rl +62 -9
- data/java/src/json/ext/Generator.java +33 -10
- data/java/src/json/ext/Parser.java +95 -80
- data/java/src/json/ext/Parser.rl +25 -10
- data/json-java.gemspec +4 -5
- data/json.gemspec +0 -0
- data/json_pure.gemspec +9 -14
- data/lib/json.rb +378 -29
- data/lib/json/add/bigdecimal.rb +2 -2
- data/lib/json/add/complex.rb +2 -2
- data/lib/json/add/ostruct.rb +1 -1
- data/lib/json/add/rational.rb +2 -2
- data/lib/json/add/regexp.rb +2 -2
- data/lib/json/add/set.rb +29 -0
- data/lib/json/common.rb +326 -91
- data/lib/json/pure/generator.rb +3 -2
- data/lib/json/pure/parser.rb +15 -3
- data/lib/json/version.rb +1 -1
- data/tests/json_addition_test.rb +10 -0
- data/tests/json_common_interface_test.rb +4 -4
- data/tests/json_encoding_test.rb +2 -0
- data/tests/json_fixtures_test.rb +6 -1
- data/tests/json_generator_test.rb +44 -0
- data/tests/json_parser_test.rb +18 -12
- data/tests/test_helper.rb +0 -4
- metadata +24 -16
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
data/java/src/json/ext/Parser.rl
CHANGED
|
@@ -52,7 +52,8 @@ public class Parser extends RubyObject {
|
|
|
52
52
|
private boolean symbolizeNames;
|
|
53
53
|
private RubyClass objectClass;
|
|
54
54
|
private RubyClass arrayClass;
|
|
55
|
-
private
|
|
55
|
+
private RubyClass decimalClass;
|
|
56
|
+
private RubyHash match_string;
|
|
56
57
|
|
|
57
58
|
private static final int DEFAULT_MAX_NESTING = 100;
|
|
58
59
|
|
|
@@ -131,6 +132,10 @@ public class Parser extends RubyObject {
|
|
|
131
132
|
* <dt><code>:array_class</code>
|
|
132
133
|
* <dd>Defaults to Array.
|
|
133
134
|
*
|
|
135
|
+
* <dt><code>:decimal_class</code>
|
|
136
|
+
* <dd>Specifies which class to use instead of the default (Float) when
|
|
137
|
+
* parsing decimal numbers. This class must accept a single string argument
|
|
138
|
+
* in its constructor.
|
|
134
139
|
* </dl>
|
|
135
140
|
*/
|
|
136
141
|
@JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
|
|
@@ -157,7 +162,8 @@ public class Parser extends RubyObject {
|
|
|
157
162
|
this.createAdditions = opts.getBool("create_additions", false);
|
|
158
163
|
this.objectClass = opts.getClass("object_class", runtime.getHash());
|
|
159
164
|
this.arrayClass = opts.getClass("array_class", runtime.getArray());
|
|
160
|
-
this.
|
|
165
|
+
this.decimalClass = opts.getClass("decimal_class", null);
|
|
166
|
+
this.match_string = opts.getHash("match_string");
|
|
161
167
|
|
|
162
168
|
if(symbolizeNames && createAdditions) {
|
|
163
169
|
throw runtime.newArgumentError(
|
|
@@ -489,13 +495,13 @@ public class Parser extends RubyObject {
|
|
|
489
495
|
|
|
490
496
|
return p;
|
|
491
497
|
}
|
|
492
|
-
|
|
498
|
+
|
|
493
499
|
RubyInteger createInteger(int p, int new_p) {
|
|
494
500
|
Ruby runtime = getRuntime();
|
|
495
501
|
ByteList num = absSubSequence(p, new_p);
|
|
496
502
|
return bytesToInum(runtime, num);
|
|
497
503
|
}
|
|
498
|
-
|
|
504
|
+
|
|
499
505
|
RubyInteger bytesToInum(Ruby runtime, ByteList num) {
|
|
500
506
|
return runtime.is1_9() ?
|
|
501
507
|
ConvertBytes.byteListToInum19(runtime, num, 10, true) :
|
|
@@ -525,7 +531,9 @@ public class Parser extends RubyObject {
|
|
|
525
531
|
res.update(null, p);
|
|
526
532
|
return;
|
|
527
533
|
}
|
|
528
|
-
|
|
534
|
+
IRubyObject number = parser.decimalClass == null ?
|
|
535
|
+
createFloat(p, new_p) : createCustomDecimal(p, new_p);
|
|
536
|
+
|
|
529
537
|
res.update(number, new_p + 1);
|
|
530
538
|
return;
|
|
531
539
|
}
|
|
@@ -540,16 +548,23 @@ public class Parser extends RubyObject {
|
|
|
540
548
|
if (cs < JSON_float_first_final) {
|
|
541
549
|
return -1;
|
|
542
550
|
}
|
|
543
|
-
|
|
551
|
+
|
|
544
552
|
return p;
|
|
545
553
|
}
|
|
546
|
-
|
|
554
|
+
|
|
547
555
|
RubyFloat createFloat(int p, int new_p) {
|
|
548
556
|
Ruby runtime = getRuntime();
|
|
549
557
|
ByteList num = absSubSequence(p, new_p);
|
|
550
558
|
return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
|
|
551
559
|
}
|
|
552
560
|
|
|
561
|
+
IRubyObject createCustomDecimal(int p, int new_p) {
|
|
562
|
+
Ruby runtime = getRuntime();
|
|
563
|
+
ByteList num = absSubSequence(p, new_p);
|
|
564
|
+
IRubyObject numString = runtime.newString(num.toString());
|
|
565
|
+
return parser.decimalClass.callMethod(context, "new", numString);
|
|
566
|
+
}
|
|
567
|
+
|
|
553
568
|
%%{
|
|
554
569
|
machine JSON_string;
|
|
555
570
|
include JSON_common;
|
|
@@ -592,7 +607,7 @@ public class Parser extends RubyObject {
|
|
|
592
607
|
%% write exec;
|
|
593
608
|
|
|
594
609
|
if (parser.createAdditions) {
|
|
595
|
-
RubyHash matchString = parser.
|
|
610
|
+
RubyHash matchString = parser.match_string;
|
|
596
611
|
if (matchString != null) {
|
|
597
612
|
final IRubyObject[] memoArray = { result, null };
|
|
598
613
|
try {
|
|
@@ -616,7 +631,7 @@ public class Parser extends RubyObject {
|
|
|
616
631
|
}
|
|
617
632
|
}
|
|
618
633
|
|
|
619
|
-
if (cs >= JSON_string_first_final && result != null) {
|
|
634
|
+
if (cs >= JSON_string_first_final && result != null) {
|
|
620
635
|
if (result instanceof RubyString) {
|
|
621
636
|
((RubyString)result).force_encoding(context, info.utf8.get());
|
|
622
637
|
}
|
|
@@ -734,7 +749,7 @@ public class Parser extends RubyObject {
|
|
|
734
749
|
fhold;
|
|
735
750
|
fbreak;
|
|
736
751
|
}
|
|
737
|
-
|
|
752
|
+
|
|
738
753
|
pair = ignore* begin_name >parse_name ignore* name_separator
|
|
739
754
|
ignore* begin_value >parse_value;
|
|
740
755
|
next_pair = ignore* value_separator pair;
|
data/json-java.gemspec
CHANGED
|
@@ -8,9 +8,8 @@ spec = Gem::Specification.new do |s|
|
|
|
8
8
|
s.description = "A JSON implementation as a JRuby extension."
|
|
9
9
|
s.author = "Daniel Luz"
|
|
10
10
|
s.email = "dev+ruby@mernen.com"
|
|
11
|
-
s.homepage = "http://
|
|
11
|
+
s.homepage = "http://flori.github.com/json"
|
|
12
12
|
s.platform = 'java'
|
|
13
|
-
s.rubyforge_project = "json-jruby"
|
|
14
13
|
s.licenses = ["Ruby"]
|
|
15
14
|
|
|
16
15
|
s.files = Dir["{docs,lib,tests}/**/*"]
|
|
@@ -20,14 +19,14 @@ spec = Gem::Specification.new do |s|
|
|
|
20
19
|
|
|
21
20
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
22
21
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
|
23
|
-
s.add_development_dependency(%q<test-unit>, ["
|
|
22
|
+
s.add_development_dependency(%q<test-unit>, [">= 2.0", "< 4.0"])
|
|
24
23
|
else
|
|
25
24
|
s.add_dependency(%q<rake>, [">= 0"])
|
|
26
|
-
s.add_dependency(%q<test-unit>, ["
|
|
25
|
+
s.add_dependency(%q<test-unit>, [">= 2.0", "< 4.0"])
|
|
27
26
|
end
|
|
28
27
|
else
|
|
29
28
|
s.add_dependency(%q<rake>, [">= 0"])
|
|
30
|
-
s.add_dependency(%q<test-unit>, ["
|
|
29
|
+
s.add_dependency(%q<test-unit>, [">= 2.0", "< 4.0"])
|
|
31
30
|
end
|
|
32
31
|
end
|
|
33
32
|
|
data/json.gemspec
CHANGED
|
Binary file
|
data/json_pure.gemspec
CHANGED
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: json_pure 2.0.3 ruby lib
|
|
3
2
|
|
|
4
3
|
Gem::Specification.new do |s|
|
|
5
4
|
s.name = "json_pure".freeze
|
|
6
|
-
s.version = "
|
|
5
|
+
s.version = File.read("VERSION").chomp
|
|
7
6
|
|
|
8
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
8
|
s.require_paths = ["lib".freeze]
|
|
10
9
|
s.authors = ["Florian Frank".freeze]
|
|
11
|
-
s.date = "2017-01-12"
|
|
12
10
|
s.description = "This is a JSON implementation in pure Ruby.".freeze
|
|
13
11
|
s.email = "flori@ping.de".freeze
|
|
14
12
|
s.extra_rdoc_files = ["README.md".freeze]
|
|
15
|
-
s.files = ["./tests/test_helper.rb".freeze, ".gitignore".freeze, ".travis.yml".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "README-json-jruby.md".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "
|
|
13
|
+
s.files = ["./tests/test_helper.rb".freeze, ".gitignore".freeze, ".travis.yml".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README-json-jruby.md".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "diagrams/.keep".freeze, "ext/json/ext/fbuffer/fbuffer.h".freeze, "ext/json/ext/generator/depend".freeze, "ext/json/ext/generator/extconf.rb".freeze, "ext/json/ext/generator/generator.c".freeze, "ext/json/ext/generator/generator.h".freeze, "ext/json/ext/parser/depend".freeze, "ext/json/ext/parser/extconf.rb".freeze, "ext/json/ext/parser/parser.c".freeze, "ext/json/ext/parser/parser.h".freeze, "ext/json/ext/parser/parser.rl".freeze, "ext/json/extconf.rb".freeze, "install.rb".freeze, "java/src/json/ext/ByteListTranscoder.java".freeze, "java/src/json/ext/Generator.java".freeze, "java/src/json/ext/GeneratorMethods.java".freeze, "java/src/json/ext/GeneratorService.java".freeze, "java/src/json/ext/GeneratorState.java".freeze, "java/src/json/ext/OptionsReader.java".freeze, "java/src/json/ext/Parser.java".freeze, "java/src/json/ext/Parser.rl".freeze, "java/src/json/ext/ParserService.java".freeze, "java/src/json/ext/RuntimeInfo.java".freeze, "java/src/json/ext/StringDecoder.java".freeze, "java/src/json/ext/StringEncoder.java".freeze, "java/src/json/ext/Utils.java".freeze, "json-java.gemspec".freeze, "json.gemspec".freeze, "json_pure.gemspec".freeze, "lib/json.rb".freeze, "lib/json/add/bigdecimal.rb".freeze, "lib/json/add/complex.rb".freeze, "lib/json/add/core.rb".freeze, "lib/json/add/date.rb".freeze, "lib/json/add/date_time.rb".freeze, "lib/json/add/exception.rb".freeze, "lib/json/add/ostruct.rb".freeze, "lib/json/add/range.rb".freeze, "lib/json/add/rational.rb".freeze, "lib/json/add/regexp.rb".freeze, "lib/json/add/set.rb".freeze, "lib/json/add/struct.rb".freeze, "lib/json/add/symbol.rb".freeze, "lib/json/add/time.rb".freeze, "lib/json/common.rb".freeze, "lib/json/ext.rb".freeze, "lib/json/ext/.keep".freeze, "lib/json/generic_object.rb".freeze, "lib/json/pure.rb".freeze, "lib/json/pure/generator.rb".freeze, "lib/json/pure/parser.rb".freeze, "lib/json/version.rb".freeze, "references/rfc7159.txt".freeze, "tests/fixtures/fail10.json".freeze, "tests/fixtures/fail11.json".freeze, "tests/fixtures/fail12.json".freeze, "tests/fixtures/fail13.json".freeze, "tests/fixtures/fail14.json".freeze, "tests/fixtures/fail18.json".freeze, "tests/fixtures/fail19.json".freeze, "tests/fixtures/fail2.json".freeze, "tests/fixtures/fail20.json".freeze, "tests/fixtures/fail21.json".freeze, "tests/fixtures/fail22.json".freeze, "tests/fixtures/fail23.json".freeze, "tests/fixtures/fail24.json".freeze, "tests/fixtures/fail25.json".freeze, "tests/fixtures/fail27.json".freeze, "tests/fixtures/fail28.json".freeze, "tests/fixtures/fail3.json".freeze, "tests/fixtures/fail4.json".freeze, "tests/fixtures/fail5.json".freeze, "tests/fixtures/fail6.json".freeze, "tests/fixtures/fail7.json".freeze, "tests/fixtures/fail8.json".freeze, "tests/fixtures/fail9.json".freeze, "tests/fixtures/obsolete_fail1.json".freeze, "tests/fixtures/pass1.json".freeze, "tests/fixtures/pass15.json".freeze, "tests/fixtures/pass16.json".freeze, "tests/fixtures/pass17.json".freeze, "tests/fixtures/pass2.json".freeze, "tests/fixtures/pass26.json".freeze, "tests/fixtures/pass3.json".freeze, "tests/json_addition_test.rb".freeze, "tests/json_common_interface_test.rb".freeze, "tests/json_encoding_test.rb".freeze, "tests/json_ext_parser_test.rb".freeze, "tests/json_fixtures_test.rb".freeze, "tests/json_generator_test.rb".freeze, "tests/json_generic_object_test.rb".freeze, "tests/json_parser_test.rb".freeze, "tests/json_string_matching_test.rb".freeze, "tests/test_helper.rb".freeze, "tools/diff.sh".freeze, "tools/fuzz.rb".freeze, "tools/server.rb".freeze]
|
|
16
14
|
s.homepage = "http://flori.github.com/json".freeze
|
|
17
15
|
s.licenses = ["Ruby".freeze]
|
|
18
16
|
s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze]
|
|
19
|
-
s.required_ruby_version = Gem::Requirement.new(">=
|
|
20
|
-
s.rubygems_version = "
|
|
17
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
|
|
18
|
+
s.rubygems_version = "3.1.2".freeze
|
|
21
19
|
s.summary = "JSON Implementation for Ruby".freeze
|
|
22
20
|
s.test_files = ["./tests/test_helper.rb".freeze]
|
|
23
21
|
|
|
24
22
|
if s.respond_to? :specification_version then
|
|
25
23
|
s.specification_version = 4
|
|
24
|
+
end
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
else
|
|
31
|
-
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
|
32
|
-
s.add_dependency(%q<test-unit>.freeze, ["~> 2.0"])
|
|
33
|
-
end
|
|
26
|
+
if s.respond_to? :add_runtime_dependency then
|
|
27
|
+
s.add_development_dependency(%q<rake>.freeze, [">= 0"])
|
|
28
|
+
s.add_development_dependency(%q<test-unit>.freeze, [">= 2.0", "< 4.0"])
|
|
34
29
|
else
|
|
35
30
|
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
|
36
|
-
s.add_dependency(%q<test-unit>.freeze, ["
|
|
31
|
+
s.add_dependency(%q<test-unit>.freeze, [">= 2.0", "< 4.0"])
|
|
37
32
|
end
|
|
38
33
|
end
|
data/lib/json.rb
CHANGED
|
@@ -2,55 +2,404 @@
|
|
|
2
2
|
require 'json/common'
|
|
3
3
|
|
|
4
4
|
##
|
|
5
|
-
# = JavaScript Object Notation (JSON)
|
|
5
|
+
# = JavaScript \Object Notation (\JSON)
|
|
6
6
|
#
|
|
7
|
-
# JSON is a lightweight data-interchange format.
|
|
8
|
-
# humans to read and write. Plus, equally simple for machines to generate or parse.
|
|
9
|
-
# JSON is completely language agnostic, making it the ideal interchange format.
|
|
7
|
+
# \JSON is a lightweight data-interchange format.
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
9
|
+
# A \JSON value is one of the following:
|
|
10
|
+
# - Double-quoted text: <tt>"foo"</tt>.
|
|
11
|
+
# - Number: +1+, +1.0+, +2.0e2+.
|
|
12
|
+
# - Boolean: +true+, +false+.
|
|
13
|
+
# - Null: +null+.
|
|
14
|
+
# - \Array: an ordered list of values, enclosed by square brackets:
|
|
15
|
+
# ["foo", 1, 1.0, 2.0e2, true, false, null]
|
|
14
16
|
#
|
|
15
|
-
#
|
|
17
|
+
# - \Object: a collection of name/value pairs, enclosed by curly braces;
|
|
18
|
+
# each name is double-quoted text;
|
|
19
|
+
# the values may be any \JSON values:
|
|
20
|
+
# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
|
|
16
21
|
#
|
|
17
|
-
#
|
|
22
|
+
# A \JSON array or object may contain nested arrays, objects, and scalars
|
|
23
|
+
# to any depth:
|
|
24
|
+
# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
|
|
25
|
+
# [{"foo": 0, "bar": 1}, ["baz", 2]]
|
|
18
26
|
#
|
|
19
|
-
#
|
|
20
|
-
# your existing application:
|
|
27
|
+
# == Using \Module \JSON
|
|
21
28
|
#
|
|
29
|
+
# To make module \JSON available in your code, begin with:
|
|
22
30
|
# require 'json'
|
|
23
31
|
#
|
|
24
|
-
#
|
|
25
|
-
# puts my_hash["hello"] => "goodbye"
|
|
32
|
+
# All examples here assume that this has been done.
|
|
26
33
|
#
|
|
27
|
-
#
|
|
28
|
-
# the argument to be a string and can't convert objects like a hash or array.
|
|
34
|
+
# === Parsing \JSON
|
|
29
35
|
#
|
|
30
|
-
#
|
|
36
|
+
# You can parse a \String containing \JSON data using
|
|
37
|
+
# either of two methods:
|
|
38
|
+
# - <tt>JSON.parse(source, opts)</tt>
|
|
39
|
+
# - <tt>JSON.parse!(source, opts)</tt>
|
|
31
40
|
#
|
|
32
|
-
#
|
|
41
|
+
# where
|
|
42
|
+
# - +source+ is a Ruby object.
|
|
43
|
+
# - +opts+ is a \Hash object containing options
|
|
44
|
+
# that control both input allowed and output formatting.
|
|
33
45
|
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
46
|
+
# The difference between the two methods
|
|
47
|
+
# is that JSON.parse! omits some checks
|
|
48
|
+
# and may not be safe for some +source+ data;
|
|
49
|
+
# use it only for data from trusted sources.
|
|
50
|
+
# Use the safer method JSON.parse for less trusted sources.
|
|
36
51
|
#
|
|
37
|
-
#
|
|
52
|
+
# ==== Parsing \JSON Arrays
|
|
38
53
|
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
54
|
+
# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array:
|
|
55
|
+
# json = '["foo", 1, 1.0, 2.0e2, true, false, null]'
|
|
56
|
+
# ruby = JSON.parse(json)
|
|
57
|
+
# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil]
|
|
58
|
+
# ruby.class # => Array
|
|
41
59
|
#
|
|
42
|
-
#
|
|
60
|
+
# The \JSON array may contain nested arrays, objects, and scalars
|
|
61
|
+
# to any depth:
|
|
62
|
+
# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
|
|
63
|
+
# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
|
|
43
64
|
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
65
|
+
# ==== Parsing \JSON \Objects
|
|
66
|
+
#
|
|
67
|
+
# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash:
|
|
68
|
+
# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}'
|
|
69
|
+
# ruby = JSON.parse(json)
|
|
70
|
+
# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil}
|
|
71
|
+
# ruby.class # => Hash
|
|
72
|
+
#
|
|
73
|
+
# The \JSON object may contain nested arrays, objects, and scalars
|
|
74
|
+
# to any depth:
|
|
75
|
+
# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
|
|
76
|
+
# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
|
|
77
|
+
#
|
|
78
|
+
# ==== Parsing \JSON Scalars
|
|
79
|
+
#
|
|
80
|
+
# When the source is a \JSON scalar (not an array or object),
|
|
81
|
+
# JSON.parse returns a Ruby scalar.
|
|
82
|
+
#
|
|
83
|
+
# \String:
|
|
84
|
+
# ruby = JSON.parse('"foo"')
|
|
85
|
+
# ruby # => 'foo'
|
|
86
|
+
# ruby.class # => String
|
|
87
|
+
# \Integer:
|
|
88
|
+
# ruby = JSON.parse('1')
|
|
89
|
+
# ruby # => 1
|
|
90
|
+
# ruby.class # => Integer
|
|
91
|
+
# \Float:
|
|
92
|
+
# ruby = JSON.parse('1.0')
|
|
93
|
+
# ruby # => 1.0
|
|
94
|
+
# ruby.class # => Float
|
|
95
|
+
# ruby = JSON.parse('2.0e2')
|
|
96
|
+
# ruby # => 200
|
|
97
|
+
# ruby.class # => Float
|
|
98
|
+
# Boolean:
|
|
99
|
+
# ruby = JSON.parse('true')
|
|
100
|
+
# ruby # => true
|
|
101
|
+
# ruby.class # => TrueClass
|
|
102
|
+
# ruby = JSON.parse('false')
|
|
103
|
+
# ruby # => false
|
|
104
|
+
# ruby.class # => FalseClass
|
|
105
|
+
# Null:
|
|
106
|
+
# ruby = JSON.parse('null')
|
|
107
|
+
# ruby # => nil
|
|
108
|
+
# ruby.class # => NilClass
|
|
109
|
+
#
|
|
110
|
+
# === Generating \JSON
|
|
111
|
+
#
|
|
112
|
+
# To generate a Ruby \String containing \JSON data,
|
|
113
|
+
# use method <tt>JSON.generate(source, opts)</tt>, where
|
|
114
|
+
# - +source+ is a Ruby object.
|
|
115
|
+
# - +opts+ is a \Hash object containing options
|
|
116
|
+
# that control both input allowed and output formatting.
|
|
117
|
+
#
|
|
118
|
+
# ==== Generating \JSON from Arrays
|
|
119
|
+
#
|
|
120
|
+
# When the source is a Ruby \Array, JSON.generate returns
|
|
121
|
+
# a \String containing a \JSON array:
|
|
122
|
+
# ruby = [0, 's', :foo]
|
|
123
|
+
# json = JSON.generate(ruby)
|
|
124
|
+
# json # => '[0,"s","foo"]'
|
|
125
|
+
#
|
|
126
|
+
# The Ruby \Array array may contain nested arrays, hashes, and scalars
|
|
127
|
+
# to any depth:
|
|
128
|
+
# ruby = [0, [1, 2], {foo: 3, bar: 4}]
|
|
129
|
+
# json = JSON.generate(ruby)
|
|
130
|
+
# json # => '[0,[1,2],{"foo":3,"bar":4}]'
|
|
131
|
+
#
|
|
132
|
+
# ==== Generating \JSON from Hashes
|
|
133
|
+
#
|
|
134
|
+
# When the source is a Ruby \Hash, JSON.generate returns
|
|
135
|
+
# a \String containing a \JSON object:
|
|
136
|
+
# ruby = {foo: 0, bar: 's', baz: :bat}
|
|
137
|
+
# json = JSON.generate(ruby)
|
|
138
|
+
# json # => '{"foo":0,"bar":"s","baz":"bat"}'
|
|
139
|
+
#
|
|
140
|
+
# The Ruby \Hash array may contain nested arrays, hashes, and scalars
|
|
141
|
+
# to any depth:
|
|
142
|
+
# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
|
|
143
|
+
# json = JSON.generate(ruby)
|
|
144
|
+
# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
|
|
145
|
+
#
|
|
146
|
+
# ==== Generating \JSON from Other Objects
|
|
147
|
+
#
|
|
148
|
+
# When the source is neither an \Array nor a \Hash,
|
|
149
|
+
# the generated \JSON data depends on the class of the source.
|
|
150
|
+
#
|
|
151
|
+
# When the source is a Ruby \Integer or \Float, JSON.generate returns
|
|
152
|
+
# a \String containing a \JSON number:
|
|
153
|
+
# JSON.generate(42) # => '42'
|
|
154
|
+
# JSON.generate(0.42) # => '0.42'
|
|
155
|
+
#
|
|
156
|
+
# When the source is a Ruby \String, JSON.generate returns
|
|
157
|
+
# a \String containing a \JSON string (with double-quotes):
|
|
158
|
+
# JSON.generate('A string') # => '"A string"'
|
|
159
|
+
#
|
|
160
|
+
# When the source is +true+, +false+ or +nil+, JSON.generate returns
|
|
161
|
+
# a \String containing the corresponding \JSON token:
|
|
162
|
+
# JSON.generate(true) # => 'true'
|
|
163
|
+
# JSON.generate(false) # => 'false'
|
|
164
|
+
# JSON.generate(nil) # => 'null'
|
|
165
|
+
#
|
|
166
|
+
# When the source is none of the above, JSON.generate returns
|
|
167
|
+
# a \String containing a \JSON string representation of the source:
|
|
168
|
+
# JSON.generate(:foo) # => '"foo"'
|
|
169
|
+
# JSON.generate(Complex(0, 0)) # => '"0+0i"'
|
|
170
|
+
# JSON.generate(Dir.new('.')) # => '"#<Dir>"'
|
|
171
|
+
#
|
|
172
|
+
# == \JSON Additions
|
|
173
|
+
#
|
|
174
|
+
# When you "round trip" a non-\String object from Ruby to \JSON and back,
|
|
175
|
+
# you have a new \String, instead of the object you began with:
|
|
176
|
+
# ruby0 = Range.new(0, 2)
|
|
177
|
+
# json = JSON.generate(ruby0)
|
|
178
|
+
# json # => '0..2"'
|
|
179
|
+
# ruby1 = JSON.parse(json)
|
|
180
|
+
# ruby1 # => '0..2'
|
|
181
|
+
# ruby1.class # => String
|
|
182
|
+
#
|
|
183
|
+
# You can use \JSON _additions_ to preserve the original object.
|
|
184
|
+
# The addition is an extension of a ruby class, so that:
|
|
185
|
+
# - \JSON.generate stores more information in the \JSON string.
|
|
186
|
+
# - \JSON.parse, called with option +create_additions+,
|
|
187
|
+
# uses that information to create a proper Ruby object.
|
|
46
188
|
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
189
|
+
# This example shows a \Range being generated into \JSON
|
|
190
|
+
# and parsed back into Ruby, both without and with
|
|
191
|
+
# the addition for \Range:
|
|
192
|
+
# ruby = Range.new(0, 2)
|
|
193
|
+
# # This passage does not use the addition for Range.
|
|
194
|
+
# json0 = JSON.generate(ruby)
|
|
195
|
+
# ruby0 = JSON.parse(json0)
|
|
196
|
+
# # This passage uses the addition for Range.
|
|
197
|
+
# require 'json/add/range'
|
|
198
|
+
# json1 = JSON.generate(ruby)
|
|
199
|
+
# ruby1 = JSON.parse(json1, create_additions: true)
|
|
200
|
+
# # Make a nice display.
|
|
201
|
+
# display = <<EOT
|
|
202
|
+
# Generated JSON:
|
|
203
|
+
# Without addition: #{json0} (#{json0.class})
|
|
204
|
+
# With addition: #{json1} (#{json1.class})
|
|
205
|
+
# Parsed JSON:
|
|
206
|
+
# Without addition: #{ruby0.inspect} (#{ruby0.class})
|
|
207
|
+
# With addition: #{ruby1.inspect} (#{ruby1.class})
|
|
208
|
+
# EOT
|
|
209
|
+
# puts display
|
|
50
210
|
#
|
|
211
|
+
# This output shows the different results:
|
|
212
|
+
# Generated JSON:
|
|
213
|
+
# Without addition: "0..2" (String)
|
|
214
|
+
# With addition: {"json_class":"Range","a":[0,2,false]} (String)
|
|
215
|
+
# Parsed JSON:
|
|
216
|
+
# Without addition: "0..2" (String)
|
|
217
|
+
# With addition: 0..2 (Range)
|
|
218
|
+
#
|
|
219
|
+
# The \JSON module includes additions for certain classes.
|
|
220
|
+
# You can also craft custom additions.
|
|
221
|
+
# See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions].
|
|
222
|
+
#
|
|
223
|
+
# === Built-in Additions
|
|
224
|
+
#
|
|
225
|
+
# The \JSON module includes additions for certain classes.
|
|
226
|
+
# To use an addition, +require+ its source:
|
|
227
|
+
# - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
|
|
228
|
+
# - Complex: <tt>require 'json/add/complex'</tt>
|
|
229
|
+
# - Date: <tt>require 'json/add/date'</tt>
|
|
230
|
+
# - DateTime: <tt>require 'json/add/date_time'</tt>
|
|
231
|
+
# - Exception: <tt>require 'json/add/exception'</tt>
|
|
232
|
+
# - OpenStruct: <tt>require 'json/add/ostruct'</tt>
|
|
233
|
+
# - Range: <tt>require 'json/add/range'</tt>
|
|
234
|
+
# - Rational: <tt>require 'json/add/rational'</tt>
|
|
235
|
+
# - Regexp: <tt>require 'json/add/regexp'</tt>
|
|
236
|
+
# - Set: <tt>require 'json/add/set'</tt>
|
|
237
|
+
# - Struct: <tt>require 'json/add/struct'</tt>
|
|
238
|
+
# - Symbol: <tt>require 'json/add/symbol'</tt>
|
|
239
|
+
# - Time: <tt>require 'json/add/time'</tt>
|
|
240
|
+
#
|
|
241
|
+
# To reduce punctuation clutter, the examples below
|
|
242
|
+
# show the generated \JSON via +puts+, rather than the usual +inspect+,
|
|
243
|
+
#
|
|
244
|
+
# \BigDecimal:
|
|
245
|
+
# require 'json/add/bigdecimal'
|
|
246
|
+
# ruby0 = BigDecimal(0) # 0.0
|
|
247
|
+
# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"}
|
|
248
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 0.0
|
|
249
|
+
# ruby1.class # => BigDecimal
|
|
250
|
+
#
|
|
251
|
+
# \Complex:
|
|
252
|
+
# require 'json/add/complex'
|
|
253
|
+
# ruby0 = Complex(1+0i) # 1+0i
|
|
254
|
+
# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0}
|
|
255
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 1+0i
|
|
256
|
+
# ruby1.class # Complex
|
|
257
|
+
#
|
|
258
|
+
# \Date:
|
|
259
|
+
# require 'json/add/date'
|
|
260
|
+
# ruby0 = Date.today # 2020-05-02
|
|
261
|
+
# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0}
|
|
262
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02
|
|
263
|
+
# ruby1.class # Date
|
|
264
|
+
#
|
|
265
|
+
# \DateTime:
|
|
266
|
+
# require 'json/add/date_time'
|
|
267
|
+
# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00
|
|
268
|
+
# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0}
|
|
269
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00
|
|
270
|
+
# ruby1.class # DateTime
|
|
271
|
+
#
|
|
272
|
+
# \Exception (and its subclasses including \RuntimeError):
|
|
273
|
+
# require 'json/add/exception'
|
|
274
|
+
# ruby0 = Exception.new('A message') # A message
|
|
275
|
+
# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null}
|
|
276
|
+
# ruby1 = JSON.parse(json, create_additions: true) # A message
|
|
277
|
+
# ruby1.class # Exception
|
|
278
|
+
# ruby0 = RuntimeError.new('Another message') # Another message
|
|
279
|
+
# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null}
|
|
280
|
+
# ruby1 = JSON.parse(json, create_additions: true) # Another message
|
|
281
|
+
# ruby1.class # RuntimeError
|
|
282
|
+
#
|
|
283
|
+
# \OpenStruct:
|
|
284
|
+
# require 'json/add/ostruct'
|
|
285
|
+
# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby">
|
|
286
|
+
# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
|
|
287
|
+
# ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
|
|
288
|
+
# ruby1.class # OpenStruct
|
|
289
|
+
#
|
|
290
|
+
# \Range:
|
|
291
|
+
# require 'json/add/range'
|
|
292
|
+
# ruby0 = Range.new(0, 2) # 0..2
|
|
293
|
+
# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]}
|
|
294
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 0..2
|
|
295
|
+
# ruby1.class # Range
|
|
296
|
+
#
|
|
297
|
+
# \Rational:
|
|
298
|
+
# require 'json/add/rational'
|
|
299
|
+
# ruby0 = Rational(1, 3) # 1/3
|
|
300
|
+
# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3}
|
|
301
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 1/3
|
|
302
|
+
# ruby1.class # Rational
|
|
303
|
+
#
|
|
304
|
+
# \Regexp:
|
|
305
|
+
# require 'json/add/regexp'
|
|
306
|
+
# ruby0 = Regexp.new('foo') # (?-mix:foo)
|
|
307
|
+
# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"}
|
|
308
|
+
# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo)
|
|
309
|
+
# ruby1.class # Regexp
|
|
310
|
+
#
|
|
311
|
+
# \Set:
|
|
312
|
+
# require 'json/add/set'
|
|
313
|
+
# ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}>
|
|
314
|
+
# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
|
|
315
|
+
# ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
|
|
316
|
+
# ruby1.class # Set
|
|
317
|
+
#
|
|
318
|
+
# \Struct:
|
|
319
|
+
# require 'json/add/struct'
|
|
320
|
+
# Customer = Struct.new(:name, :address) # Customer
|
|
321
|
+
# ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
|
|
322
|
+
# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
|
|
323
|
+
# ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
|
|
324
|
+
# ruby1.class # Customer
|
|
325
|
+
#
|
|
326
|
+
# \Symbol:
|
|
327
|
+
# require 'json/add/symbol'
|
|
328
|
+
# ruby0 = :foo # foo
|
|
329
|
+
# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
|
|
330
|
+
# ruby1 = JSON.parse(json, create_additions: true) # foo
|
|
331
|
+
# ruby1.class # Symbol
|
|
332
|
+
#
|
|
333
|
+
# \Time:
|
|
334
|
+
# require 'json/add/time'
|
|
335
|
+
# ruby0 = Time.now # 2020-05-02 11:28:26 -0500
|
|
336
|
+
# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
|
|
337
|
+
# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
|
|
338
|
+
# ruby1.class # Time
|
|
339
|
+
#
|
|
340
|
+
#
|
|
341
|
+
# === Custom \JSON Additions
|
|
342
|
+
#
|
|
343
|
+
# In addition to the \JSON additions provided,
|
|
344
|
+
# you can craft \JSON additions of your own,
|
|
345
|
+
# either for Ruby built-in classes or for user-defined classes.
|
|
346
|
+
#
|
|
347
|
+
# Here's a user-defined class +Foo+:
|
|
348
|
+
# class Foo
|
|
349
|
+
# attr_accessor :bar, :baz
|
|
350
|
+
# def initialize(bar, baz)
|
|
351
|
+
# self.bar = bar
|
|
352
|
+
# self.baz = baz
|
|
353
|
+
# end
|
|
354
|
+
# end
|
|
355
|
+
#
|
|
356
|
+
# Here's the \JSON addition for it:
|
|
357
|
+
# # Extend class Foo with JSON addition.
|
|
358
|
+
# class Foo
|
|
359
|
+
# # Serialize Foo object with its class name and arguments
|
|
360
|
+
# def to_json(*args)
|
|
361
|
+
# {
|
|
362
|
+
# JSON.create_id => self.class.name,
|
|
363
|
+
# 'a' => [ bar, baz ]
|
|
364
|
+
# }.to_json(*args)
|
|
365
|
+
# end
|
|
366
|
+
# # Deserialize JSON string by constructing new Foo object with arguments.
|
|
367
|
+
# def self.json_create(object)
|
|
368
|
+
# new(*object['a'])
|
|
369
|
+
# end
|
|
370
|
+
# end
|
|
371
|
+
#
|
|
372
|
+
# Demonstration:
|
|
51
373
|
# require 'json'
|
|
374
|
+
# # This Foo object has no custom addition.
|
|
375
|
+
# foo0 = Foo.new(0, 1)
|
|
376
|
+
# json0 = JSON.generate(foo0)
|
|
377
|
+
# obj0 = JSON.parse(json0)
|
|
378
|
+
# # Lood the custom addition.
|
|
379
|
+
# require_relative 'foo_addition'
|
|
380
|
+
# # This foo has the custom addition.
|
|
381
|
+
# foo1 = Foo.new(0, 1)
|
|
382
|
+
# json1 = JSON.generate(foo1)
|
|
383
|
+
# obj1 = JSON.parse(json1, create_additions: true)
|
|
384
|
+
# # Make a nice display.
|
|
385
|
+
# display = <<EOT
|
|
386
|
+
# Generated JSON:
|
|
387
|
+
# Without custom addition: #{json0} (#{json0.class})
|
|
388
|
+
# With custom addition: #{json1} (#{json1.class})
|
|
389
|
+
# Parsed JSON:
|
|
390
|
+
# Without custom addition: #{obj0.inspect} (#{obj0.class})
|
|
391
|
+
# With custom addition: #{obj1.inspect} (#{obj1.class})
|
|
392
|
+
# EOT
|
|
393
|
+
# puts display
|
|
394
|
+
#
|
|
395
|
+
# Output:
|
|
52
396
|
#
|
|
53
|
-
#
|
|
397
|
+
# Generated JSON:
|
|
398
|
+
# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
|
|
399
|
+
# With custom addition: {"json_class":"Foo","a":[0,1]} (String)
|
|
400
|
+
# Parsed JSON:
|
|
401
|
+
# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
|
|
402
|
+
# With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
|
|
54
403
|
#
|
|
55
404
|
module JSON
|
|
56
405
|
require 'json/version'
|