json 2.2.0 → 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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -2
- data/CHANGES.md +33 -0
- data/README.md +17 -1
- data/Rakefile +17 -91
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +165 -40
- data/ext/json/ext/parser/parser.c +83 -77
- data/ext/json/ext/parser/parser.rl +13 -7
- data/java/src/json/ext/Generator.java +33 -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/rational.rb +2 -2
- data/lib/json/add/regexp.rb +2 -2
- data/lib/json/common.rb +326 -91
- data/lib/json/pure/generator.rb +3 -2
- data/lib/json/pure/parser.rb +11 -3
- data/lib/json/version.rb +1 -1
- data/tests/json_common_interface_test.rb +4 -4
- data/tests/json_fixtures_test.rb +6 -1
- data/tests/json_generator_test.rb +44 -0
- data/tests/json_parser_test.rb +14 -14
- metadata +23 -12
@@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
|
|
25
25
|
|
26
26
|
/* unicode */
|
27
27
|
|
28
|
-
static const char digit_values[256] = {
|
28
|
+
static const signed char digit_values[256] = {
|
29
29
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
30
30
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
31
31
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
|
@@ -44,7 +44,7 @@ static const char digit_values[256] = {
|
|
44
44
|
|
45
45
|
static UTF32 unescape_unicode(const unsigned char *p)
|
46
46
|
{
|
47
|
-
char b;
|
47
|
+
signed char b;
|
48
48
|
UTF32 result = 0;
|
49
49
|
b = digit_values[p[0]];
|
50
50
|
if (b < 0) return UNI_REPLACEMENT_CHAR;
|
@@ -138,6 +138,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
|
|
138
138
|
fhold; fbreak;
|
139
139
|
} else {
|
140
140
|
if (NIL_P(json->object_class)) {
|
141
|
+
OBJ_FREEZE(last_name);
|
141
142
|
rb_hash_aset(*result, last_name, v);
|
142
143
|
} else {
|
143
144
|
rb_funcall(*result, i_aset, 2, last_name, v);
|
@@ -571,10 +572,8 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
|
|
571
572
|
|
572
573
|
if (json->symbolize_names && json->parsing_name) {
|
573
574
|
*result = rb_str_intern(*result);
|
574
|
-
} else {
|
575
|
-
|
576
|
-
rb_str_resize(*result, RSTRING_LEN(*result));
|
577
|
-
}
|
575
|
+
} else if (RB_TYPE_P(*result, T_STRING)) {
|
576
|
+
rb_str_resize(*result, RSTRING_LEN(*result));
|
578
577
|
}
|
579
578
|
if (cs >= JSON_string_first_final) {
|
580
579
|
return p + 1;
|
@@ -730,7 +729,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
|
|
730
729
|
} else {
|
731
730
|
json->max_nesting = 100;
|
732
731
|
json->allow_nan = 0;
|
733
|
-
json->create_additions =
|
732
|
+
json->create_additions = 0;
|
734
733
|
json->create_id = rb_funcall(mJSON, i_create_id, 0);
|
735
734
|
json->object_class = Qnil;
|
736
735
|
json->array_class = Qnil;
|
@@ -851,14 +850,21 @@ void Init_parser(void)
|
|
851
850
|
cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
|
852
851
|
eParserError = rb_path2class("JSON::ParserError");
|
853
852
|
eNestingError = rb_path2class("JSON::NestingError");
|
853
|
+
rb_gc_register_mark_object(eParserError);
|
854
|
+
rb_gc_register_mark_object(eNestingError);
|
854
855
|
rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
|
855
856
|
rb_define_method(cParser, "initialize", cParser_initialize, -1);
|
856
857
|
rb_define_method(cParser, "parse", cParser_parse, 0);
|
857
858
|
rb_define_method(cParser, "source", cParser_source, 0);
|
858
859
|
|
859
860
|
CNaN = rb_const_get(mJSON, rb_intern("NaN"));
|
861
|
+
rb_gc_register_mark_object(CNaN);
|
862
|
+
|
860
863
|
CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
|
864
|
+
rb_gc_register_mark_object(CInfinity);
|
865
|
+
|
861
866
|
CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
|
867
|
+
rb_gc_register_mark_object(CMinusInfinity);
|
862
868
|
|
863
869
|
i_json_creatable_p = rb_intern("json_creatable?");
|
864
870
|
i_json_create = rb_intern("json_create");
|
@@ -7,6 +7,7 @@ package json.ext;
|
|
7
7
|
|
8
8
|
import org.jruby.Ruby;
|
9
9
|
import org.jruby.RubyArray;
|
10
|
+
import org.jruby.RubyBasicObject;
|
10
11
|
import org.jruby.RubyBignum;
|
11
12
|
import org.jruby.RubyBoolean;
|
12
13
|
import org.jruby.RubyClass;
|
@@ -15,6 +16,7 @@ import org.jruby.RubyFloat;
|
|
15
16
|
import org.jruby.RubyHash;
|
16
17
|
import org.jruby.RubyNumeric;
|
17
18
|
import org.jruby.RubyString;
|
19
|
+
import org.jruby.runtime.ClassIndex;
|
18
20
|
import org.jruby.runtime.ThreadContext;
|
19
21
|
import org.jruby.runtime.builtin.IRubyObject;
|
20
22
|
import org.jruby.util.ByteList;
|
@@ -57,6 +59,19 @@ public final class Generator {
|
|
57
59
|
return handler.generateNew(session, object);
|
58
60
|
}
|
59
61
|
|
62
|
+
// NOTE: drop this once Ruby 1.9.3 support is gone!
|
63
|
+
private static final int FIXNUM = 1;
|
64
|
+
private static final int BIGNUM = 2;
|
65
|
+
private static final int ARRAY = 3;
|
66
|
+
private static final int STRING = 4;
|
67
|
+
private static final int NIL = 5;
|
68
|
+
private static final int TRUE = 6;
|
69
|
+
private static final int FALSE = 7;
|
70
|
+
private static final int HASH = 10;
|
71
|
+
private static final int FLOAT = 11;
|
72
|
+
// hard-coded due JRuby 1.7 compatibility
|
73
|
+
// https://github.com/jruby/jruby/blob/1.7.27/core/src/main/java/org/jruby/runtime/ClassIndex.java
|
74
|
+
|
60
75
|
/**
|
61
76
|
* Returns the best serialization handler for the given object.
|
62
77
|
*/
|
@@ -65,16 +80,24 @@ public final class Generator {
|
|
65
80
|
@SuppressWarnings("unchecked")
|
66
81
|
private static <T extends IRubyObject>
|
67
82
|
Handler<? super T> getHandlerFor(Ruby runtime, T object) {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
83
|
+
switch (((RubyBasicObject) object).getNativeTypeIndex()) {
|
84
|
+
// can not use getNativeClassIndex due 1.7 compatibility
|
85
|
+
case NIL : return (Handler) NIL_HANDLER;
|
86
|
+
case TRUE : return (Handler) TRUE_HANDLER;
|
87
|
+
case FALSE : return (Handler) FALSE_HANDLER;
|
88
|
+
case FLOAT : return (Handler) FLOAT_HANDLER;
|
89
|
+
case FIXNUM : return (Handler) FIXNUM_HANDLER;
|
90
|
+
case BIGNUM : return (Handler) BIGNUM_HANDLER;
|
91
|
+
case STRING :
|
92
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break;
|
93
|
+
return (Handler) STRING_HANDLER;
|
94
|
+
case ARRAY :
|
95
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break;
|
96
|
+
return (Handler) ARRAY_HANDLER;
|
97
|
+
case HASH :
|
98
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break;
|
99
|
+
return (Handler) HASH_HANDLER;
|
100
|
+
}
|
78
101
|
return GENERIC_HANDLER;
|
79
102
|
}
|
80
103
|
|
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.2.0 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 = "2019-02-21"
|
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'
|