json 1.8.6 → 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 +13 -5
- data/{CHANGES → CHANGES.md} +212 -95
- data/Gemfile +10 -3
- data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
- data/README.md +187 -107
- data/Rakefile +36 -104
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +175 -98
- data/ext/json/ext/generator/generator.h +0 -6
- data/ext/json/ext/parser/extconf.rb +3 -0
- data/ext/json/ext/parser/parser.c +380 -483
- data/ext/json/ext/parser/parser.h +4 -5
- data/ext/json/ext/parser/parser.rl +109 -175
- data/ext/json/extconf.rb +0 -1
- data/java/src/json/ext/Generator.java +35 -15
- data/java/src/json/ext/GeneratorState.java +2 -54
- data/java/src/json/ext/OptionsReader.java +1 -1
- data/java/src/json/ext/Parser.java +131 -413
- data/java/src/json/ext/Parser.rl +47 -122
- data/java/src/json/ext/RuntimeInfo.java +0 -4
- data/json-java.gemspec +4 -5
- data/json.gemspec +0 -0
- data/json_pure.gemspec +11 -15
- data/lib/json.rb +379 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -3
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -2
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +335 -128
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure.rb +2 -8
- data/lib/json/pure/generator.rb +54 -125
- data/lib/json/pure/parser.rb +42 -82
- data/lib/json/version.rb +2 -1
- data/references/rfc7159.txt +899 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +32 -25
- data/tests/json_common_interface_test.rb +126 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +10 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +112 -39
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +472 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/{setup_variant.rb → test_helper.rb} +6 -0
- data/tools/fuzz.rb +1 -9
- metadata +38 -43
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/tests/fixtures/fail1.json +0 -1
- data/tests/test_json.rb +0 -519
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
data/java/src/json/ext/Parser.rl
CHANGED
|
@@ -50,9 +50,9 @@ public class Parser extends RubyObject {
|
|
|
50
50
|
private int maxNesting;
|
|
51
51
|
private boolean allowNaN;
|
|
52
52
|
private boolean symbolizeNames;
|
|
53
|
-
private boolean quirksMode;
|
|
54
53
|
private RubyClass objectClass;
|
|
55
54
|
private RubyClass arrayClass;
|
|
55
|
+
private RubyClass decimalClass;
|
|
56
56
|
private RubyHash match_string;
|
|
57
57
|
|
|
58
58
|
private static final int DEFAULT_MAX_NESTING = 100;
|
|
@@ -121,10 +121,6 @@ public class Parser extends RubyObject {
|
|
|
121
121
|
* <dd>If set to <code>true</code>, returns symbols for the names (keys) in
|
|
122
122
|
* a JSON object. Otherwise strings are returned, which is also the default.
|
|
123
123
|
*
|
|
124
|
-
* <dt><code>:quirks_mode?</code>
|
|
125
|
-
* <dd>If set to <code>true</code>, if the parse is in quirks_mode, false
|
|
126
|
-
* otherwise.
|
|
127
|
-
*
|
|
128
124
|
* <dt><code>:create_additions</code>
|
|
129
125
|
* <dd>If set to <code>false</code>, the Parser doesn't create additions
|
|
130
126
|
* even if a matching class and <code>create_id</code> was found. This option
|
|
@@ -136,9 +132,10 @@ public class Parser extends RubyObject {
|
|
|
136
132
|
* <dt><code>:array_class</code>
|
|
137
133
|
* <dd>Defaults to Array.
|
|
138
134
|
*
|
|
139
|
-
* <dt><code>:
|
|
140
|
-
* <dd>
|
|
141
|
-
*
|
|
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.
|
|
142
139
|
* </dl>
|
|
143
140
|
*/
|
|
144
141
|
@JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
|
|
@@ -161,15 +158,21 @@ public class Parser extends RubyObject {
|
|
|
161
158
|
this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
|
|
162
159
|
this.allowNaN = opts.getBool("allow_nan", false);
|
|
163
160
|
this.symbolizeNames = opts.getBool("symbolize_names", false);
|
|
164
|
-
this.quirksMode = opts.getBool("quirks_mode", false);
|
|
165
161
|
this.createId = opts.getString("create_id", getCreateId(context));
|
|
166
162
|
this.createAdditions = opts.getBool("create_additions", false);
|
|
167
163
|
this.objectClass = opts.getClass("object_class", runtime.getHash());
|
|
168
164
|
this.arrayClass = opts.getClass("array_class", runtime.getArray());
|
|
165
|
+
this.decimalClass = opts.getClass("decimal_class", null);
|
|
169
166
|
this.match_string = opts.getHash("match_string");
|
|
170
167
|
|
|
168
|
+
if(symbolizeNames && createAdditions) {
|
|
169
|
+
throw runtime.newArgumentError(
|
|
170
|
+
"options :symbolize_names and :create_additions cannot be " +
|
|
171
|
+
" used in conjunction"
|
|
172
|
+
);
|
|
173
|
+
}
|
|
171
174
|
this.vSource = args[0].convertToString();
|
|
172
|
-
|
|
175
|
+
this.vSource = convertEncoding(context, vSource);
|
|
173
176
|
|
|
174
177
|
return this;
|
|
175
178
|
}
|
|
@@ -180,33 +183,16 @@ public class Parser extends RubyObject {
|
|
|
180
183
|
* Returns the source string if no conversion is needed.
|
|
181
184
|
*/
|
|
182
185
|
private RubyString convertEncoding(ThreadContext context, RubyString source) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return (RubyString)source.encode(context, info.utf8.get());
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
String sniffedEncoding = sniffByteList(bl);
|
|
197
|
-
if (sniffedEncoding == null) return source; // assume UTF-8
|
|
198
|
-
return reinterpretEncoding(context, source, sniffedEncoding);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
String sniffedEncoding = sniffByteList(bl);
|
|
202
|
-
if (sniffedEncoding == null) return source; // assume UTF-8
|
|
203
|
-
Ruby runtime = context.getRuntime();
|
|
204
|
-
return (RubyString)info.jsonModule.get().
|
|
205
|
-
callMethod(context, "iconv",
|
|
206
|
-
new IRubyObject[] {
|
|
207
|
-
runtime.newString("utf-8"),
|
|
208
|
-
runtime.newString(sniffedEncoding),
|
|
209
|
-
source});
|
|
186
|
+
RubyEncoding encoding = (RubyEncoding)source.encoding(context);
|
|
187
|
+
if (encoding == info.ascii8bit.get()) {
|
|
188
|
+
if (source.isFrozen()) {
|
|
189
|
+
source = (RubyString) source.dup();
|
|
190
|
+
}
|
|
191
|
+
source.force_encoding(context, info.utf8.get());
|
|
192
|
+
} else {
|
|
193
|
+
source = (RubyString) source.encode(context, info.utf8.get());
|
|
194
|
+
}
|
|
195
|
+
return source;
|
|
210
196
|
}
|
|
211
197
|
|
|
212
198
|
/**
|
|
@@ -259,17 +245,6 @@ public class Parser extends RubyObject {
|
|
|
259
245
|
return checkAndGetSource().dup();
|
|
260
246
|
}
|
|
261
247
|
|
|
262
|
-
/**
|
|
263
|
-
* <code>Parser#quirks_mode?()</code>
|
|
264
|
-
*
|
|
265
|
-
* <p>If set to <code>true</code>, if the parse is in quirks_mode, false
|
|
266
|
-
* otherwise.
|
|
267
|
-
*/
|
|
268
|
-
@JRubyMethod(name = "quirks_mode?")
|
|
269
|
-
public IRubyObject quirks_mode_p(ThreadContext context) {
|
|
270
|
-
return context.getRuntime().newBoolean(quirksMode);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
248
|
public RubyString checkAndGetSource() {
|
|
274
249
|
if (vSource != null) {
|
|
275
250
|
return vSource;
|
|
@@ -393,7 +368,7 @@ public class Parser extends RubyObject {
|
|
|
393
368
|
}
|
|
394
369
|
}
|
|
395
370
|
action parse_number {
|
|
396
|
-
if (pe > fpc +
|
|
371
|
+
if (pe > fpc + 8 &&
|
|
397
372
|
absSubSequence(fpc, fpc + 9).equals(JSON_MINUS_INFINITY)) {
|
|
398
373
|
|
|
399
374
|
if (parser.allowNaN) {
|
|
@@ -520,13 +495,13 @@ public class Parser extends RubyObject {
|
|
|
520
495
|
|
|
521
496
|
return p;
|
|
522
497
|
}
|
|
523
|
-
|
|
498
|
+
|
|
524
499
|
RubyInteger createInteger(int p, int new_p) {
|
|
525
500
|
Ruby runtime = getRuntime();
|
|
526
501
|
ByteList num = absSubSequence(p, new_p);
|
|
527
502
|
return bytesToInum(runtime, num);
|
|
528
503
|
}
|
|
529
|
-
|
|
504
|
+
|
|
530
505
|
RubyInteger bytesToInum(Ruby runtime, ByteList num) {
|
|
531
506
|
return runtime.is1_9() ?
|
|
532
507
|
ConvertBytes.byteListToInum19(runtime, num, 10, true) :
|
|
@@ -556,7 +531,9 @@ public class Parser extends RubyObject {
|
|
|
556
531
|
res.update(null, p);
|
|
557
532
|
return;
|
|
558
533
|
}
|
|
559
|
-
|
|
534
|
+
IRubyObject number = parser.decimalClass == null ?
|
|
535
|
+
createFloat(p, new_p) : createCustomDecimal(p, new_p);
|
|
536
|
+
|
|
560
537
|
res.update(number, new_p + 1);
|
|
561
538
|
return;
|
|
562
539
|
}
|
|
@@ -571,16 +548,23 @@ public class Parser extends RubyObject {
|
|
|
571
548
|
if (cs < JSON_float_first_final) {
|
|
572
549
|
return -1;
|
|
573
550
|
}
|
|
574
|
-
|
|
551
|
+
|
|
575
552
|
return p;
|
|
576
553
|
}
|
|
577
|
-
|
|
554
|
+
|
|
578
555
|
RubyFloat createFloat(int p, int new_p) {
|
|
579
556
|
Ruby runtime = getRuntime();
|
|
580
557
|
ByteList num = absSubSequence(p, new_p);
|
|
581
558
|
return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
|
|
582
559
|
}
|
|
583
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
|
+
|
|
584
568
|
%%{
|
|
585
569
|
machine JSON_string;
|
|
586
570
|
include JSON_common;
|
|
@@ -623,11 +607,11 @@ public class Parser extends RubyObject {
|
|
|
623
607
|
%% write exec;
|
|
624
608
|
|
|
625
609
|
if (parser.createAdditions) {
|
|
626
|
-
RubyHash
|
|
627
|
-
if (
|
|
610
|
+
RubyHash matchString = parser.match_string;
|
|
611
|
+
if (matchString != null) {
|
|
628
612
|
final IRubyObject[] memoArray = { result, null };
|
|
629
613
|
try {
|
|
630
|
-
|
|
614
|
+
matchString.visitAll(new RubyHash.Visitor() {
|
|
631
615
|
@Override
|
|
632
616
|
public void visit(IRubyObject pattern, IRubyObject klass) {
|
|
633
617
|
if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
|
|
@@ -647,8 +631,8 @@ public class Parser extends RubyObject {
|
|
|
647
631
|
}
|
|
648
632
|
}
|
|
649
633
|
|
|
650
|
-
if (cs >= JSON_string_first_final && result != null) {
|
|
651
|
-
if (
|
|
634
|
+
if (cs >= JSON_string_first_final && result != null) {
|
|
635
|
+
if (result instanceof RubyString) {
|
|
652
636
|
((RubyString)result).force_encoding(context, info.utf8.get());
|
|
653
637
|
}
|
|
654
638
|
res.update(result, p + 1);
|
|
@@ -765,7 +749,7 @@ public class Parser extends RubyObject {
|
|
|
765
749
|
fhold;
|
|
766
750
|
fbreak;
|
|
767
751
|
}
|
|
768
|
-
|
|
752
|
+
|
|
769
753
|
pair = ignore* begin_name >parse_name ignore* name_separator
|
|
770
754
|
ignore* begin_value >parse_value;
|
|
771
755
|
next_pair = ignore* value_separator pair;
|
|
@@ -835,60 +819,6 @@ public class Parser extends RubyObject {
|
|
|
835
819
|
|
|
836
820
|
write data;
|
|
837
821
|
|
|
838
|
-
action parse_object {
|
|
839
|
-
currentNesting = 1;
|
|
840
|
-
parseObject(res, fpc, pe);
|
|
841
|
-
if (res.result == null) {
|
|
842
|
-
fhold;
|
|
843
|
-
fbreak;
|
|
844
|
-
} else {
|
|
845
|
-
result = res.result;
|
|
846
|
-
fexec res.p;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
action parse_array {
|
|
851
|
-
currentNesting = 1;
|
|
852
|
-
parseArray(res, fpc, pe);
|
|
853
|
-
if (res.result == null) {
|
|
854
|
-
fhold;
|
|
855
|
-
fbreak;
|
|
856
|
-
} else {
|
|
857
|
-
result = res.result;
|
|
858
|
-
fexec res.p;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
main := ignore*
|
|
863
|
-
( begin_object >parse_object
|
|
864
|
-
| begin_array >parse_array )
|
|
865
|
-
ignore*;
|
|
866
|
-
}%%
|
|
867
|
-
|
|
868
|
-
public IRubyObject parseStrict() {
|
|
869
|
-
int cs = EVIL;
|
|
870
|
-
int p, pe;
|
|
871
|
-
IRubyObject result = null;
|
|
872
|
-
ParserResult res = new ParserResult();
|
|
873
|
-
|
|
874
|
-
%% write init;
|
|
875
|
-
p = byteList.begin();
|
|
876
|
-
pe = p + byteList.length();
|
|
877
|
-
%% write exec;
|
|
878
|
-
|
|
879
|
-
if (cs >= JSON_first_final && p == pe) {
|
|
880
|
-
return result;
|
|
881
|
-
} else {
|
|
882
|
-
throw unexpectedToken(p, pe);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
%%{
|
|
887
|
-
machine JSON_quirks_mode;
|
|
888
|
-
include JSON_common;
|
|
889
|
-
|
|
890
|
-
write data;
|
|
891
|
-
|
|
892
822
|
action parse_value {
|
|
893
823
|
parseValue(res, fpc, pe);
|
|
894
824
|
if (res.result == null) {
|
|
@@ -905,7 +835,7 @@ public class Parser extends RubyObject {
|
|
|
905
835
|
ignore*;
|
|
906
836
|
}%%
|
|
907
837
|
|
|
908
|
-
public IRubyObject
|
|
838
|
+
public IRubyObject parseImplemetation() {
|
|
909
839
|
int cs = EVIL;
|
|
910
840
|
int p, pe;
|
|
911
841
|
IRubyObject result = null;
|
|
@@ -916,7 +846,7 @@ public class Parser extends RubyObject {
|
|
|
916
846
|
pe = p + byteList.length();
|
|
917
847
|
%% write exec;
|
|
918
848
|
|
|
919
|
-
if (cs >=
|
|
849
|
+
if (cs >= JSON_first_final && p == pe) {
|
|
920
850
|
return result;
|
|
921
851
|
} else {
|
|
922
852
|
throw unexpectedToken(p, pe);
|
|
@@ -924,12 +854,7 @@ public class Parser extends RubyObject {
|
|
|
924
854
|
}
|
|
925
855
|
|
|
926
856
|
public IRubyObject parse() {
|
|
927
|
-
|
|
928
|
-
return parseQuirksMode();
|
|
929
|
-
} else {
|
|
930
|
-
return parseStrict();
|
|
931
|
-
}
|
|
932
|
-
|
|
857
|
+
return parseImplemetation();
|
|
933
858
|
}
|
|
934
859
|
|
|
935
860
|
/**
|
|
@@ -90,10 +90,6 @@ final class RuntimeInfo {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
public boolean encodingsSupported() {
|
|
94
|
-
return utf8 != null && utf8.get() != null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
93
|
public RubyEncoding getEncoding(ThreadContext context, String name) {
|
|
98
94
|
synchronized (encodings) {
|
|
99
95
|
WeakReference<RubyEncoding> encoding = encodings.get(name);
|
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,37 +1,33 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: json_pure 1.8.6 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-13"
|
|
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/
|
|
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
|
-
s.rdoc_options = ["--title".freeze, "JSON implemention for
|
|
19
|
-
s.
|
|
16
|
+
s.rdoc_options = ["--title".freeze, "JSON implemention for ruby".freeze, "--main".freeze, "README.md".freeze]
|
|
17
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
|
|
18
|
+
s.rubygems_version = "3.1.2".freeze
|
|
20
19
|
s.summary = "JSON Implementation for Ruby".freeze
|
|
21
|
-
s.test_files = ["./tests/
|
|
20
|
+
s.test_files = ["./tests/test_helper.rb".freeze]
|
|
22
21
|
|
|
23
22
|
if s.respond_to? :specification_version then
|
|
24
23
|
s.specification_version = 4
|
|
24
|
+
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
else
|
|
30
|
-
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
|
31
|
-
s.add_dependency(%q<test-unit>.freeze, ["~> 2.0"])
|
|
32
|
-
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"])
|
|
33
29
|
else
|
|
34
30
|
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
|
35
|
-
s.add_dependency(%q<test-unit>.freeze, ["
|
|
31
|
+
s.add_dependency(%q<test-unit>.freeze, [">= 2.0", "< 4.0"])
|
|
36
32
|
end
|
|
37
33
|
end
|
data/lib/json.rb
CHANGED
|
@@ -1,55 +1,405 @@
|
|
|
1
|
+
#frozen_string_literal: false
|
|
1
2
|
require 'json/common'
|
|
2
3
|
|
|
3
4
|
##
|
|
4
|
-
# = JavaScript Object Notation (JSON)
|
|
5
|
+
# = JavaScript \Object Notation (\JSON)
|
|
5
6
|
#
|
|
6
|
-
# JSON is a lightweight data-interchange format.
|
|
7
|
-
# humans to read and write. Plus, equally simple for machines to generate or parse.
|
|
8
|
-
# JSON is completely language agnostic, making it the ideal interchange format.
|
|
7
|
+
# \JSON is a lightweight data-interchange format.
|
|
9
8
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
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]
|
|
13
16
|
#
|
|
14
|
-
#
|
|
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}
|
|
15
21
|
#
|
|
16
|
-
#
|
|
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]]
|
|
17
26
|
#
|
|
18
|
-
#
|
|
19
|
-
# your existing application:
|
|
27
|
+
# == Using \Module \JSON
|
|
20
28
|
#
|
|
29
|
+
# To make module \JSON available in your code, begin with:
|
|
21
30
|
# require 'json'
|
|
22
31
|
#
|
|
23
|
-
#
|
|
24
|
-
# puts my_hash["hello"] => "goodbye"
|
|
32
|
+
# All examples here assume that this has been done.
|
|
25
33
|
#
|
|
26
|
-
#
|
|
27
|
-
# the argument to be a string and can't convert objects like a hash or array.
|
|
34
|
+
# === Parsing \JSON
|
|
28
35
|
#
|
|
29
|
-
#
|
|
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>
|
|
30
40
|
#
|
|
31
|
-
#
|
|
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.
|
|
32
45
|
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
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.
|
|
35
51
|
#
|
|
36
|
-
#
|
|
52
|
+
# ==== Parsing \JSON Arrays
|
|
37
53
|
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
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
|
|
40
59
|
#
|
|
41
|
-
#
|
|
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]]
|
|
42
64
|
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
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.
|
|
45
188
|
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
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
|
|
49
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:
|
|
50
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:
|
|
51
396
|
#
|
|
52
|
-
#
|
|
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)
|
|
53
403
|
#
|
|
54
404
|
module JSON
|
|
55
405
|
require 'json/version'
|