json 1.8.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.travis.yml +9 -12
- data/{CHANGES → CHANGES.md} +219 -90
- data/Gemfile +10 -6
- data/{COPYING-json-jruby → LICENSE} +5 -6
- data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
- data/{README.rdoc → README.md} +201 -134
- data/Rakefile +35 -113
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +255 -101
- data/ext/json/ext/generator/generator.h +12 -4
- data/ext/json/ext/parser/extconf.rb +28 -0
- data/ext/json/ext/parser/parser.c +410 -462
- data/ext/json/ext/parser/parser.h +5 -5
- data/ext/json/ext/parser/parser.rl +166 -181
- data/ext/json/extconf.rb +1 -1
- data/java/src/json/ext/ByteListTranscoder.java +1 -2
- data/java/src/json/ext/Generator.java +39 -36
- data/java/src/json/ext/GeneratorMethods.java +1 -2
- data/java/src/json/ext/GeneratorService.java +1 -2
- data/java/src/json/ext/GeneratorState.java +33 -56
- data/java/src/json/ext/OptionsReader.java +2 -3
- data/java/src/json/ext/Parser.java +146 -417
- data/java/src/json/ext/Parser.rl +62 -126
- data/java/src/json/ext/ParserService.java +1 -2
- data/java/src/json/ext/RuntimeInfo.java +1 -6
- data/java/src/json/ext/StringDecoder.java +1 -2
- data/java/src/json/ext/StringEncoder.java +13 -2
- data/java/src/json/ext/Utils.java +1 -2
- data/json-java.gemspec +22 -7
- data/json.gemspec +0 -0
- data/json_pure.gemspec +22 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -4
- 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 -3
- 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 +350 -152
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure/generator.rb +83 -126
- data/lib/json/pure/parser.rb +62 -84
- data/lib/json/pure.rb +2 -8
- data/lib/json/version.rb +2 -1
- data/lib/json.rb +550 -29
- data/references/rfc7159.txt +899 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
- data/tests/json_common_interface_test.rb +169 -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} +13 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +134 -39
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +497 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/test_helper.rb +17 -0
- data/tools/diff.sh +18 -0
- data/tools/fuzz.rb +1 -9
- metadata +47 -53
- data/COPYING +0 -58
- data/GPL +0 -340
- 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/setup_variant.rb +0 -11
- data/tests/test_json.rb +0 -553
- 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
@@ -1,20 +1,18 @@
|
|
1
1
|
/*
|
2
2
|
* This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
|
3
3
|
*
|
4
|
-
* Distributed under the Ruby
|
5
|
-
* for details.
|
4
|
+
* Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
|
6
5
|
*/
|
7
6
|
package json.ext;
|
8
7
|
|
9
8
|
import org.jruby.Ruby;
|
10
9
|
import org.jruby.RubyArray;
|
10
|
+
import org.jruby.RubyBasicObject;
|
11
11
|
import org.jruby.RubyBignum;
|
12
12
|
import org.jruby.RubyBoolean;
|
13
|
-
import org.jruby.RubyClass;
|
14
13
|
import org.jruby.RubyFixnum;
|
15
14
|
import org.jruby.RubyFloat;
|
16
15
|
import org.jruby.RubyHash;
|
17
|
-
import org.jruby.RubyNumeric;
|
18
16
|
import org.jruby.RubyString;
|
19
17
|
import org.jruby.runtime.ThreadContext;
|
20
18
|
import org.jruby.runtime.builtin.IRubyObject;
|
@@ -31,8 +29,7 @@ public final class Generator {
|
|
31
29
|
static <T extends IRubyObject> RubyString
|
32
30
|
generateJson(ThreadContext context, T object,
|
33
31
|
Handler<? super T> handler, IRubyObject[] args) {
|
34
|
-
Session session = new Session(context, args.length > 0 ? args[0]
|
35
|
-
: null);
|
32
|
+
Session session = new Session(context, args.length > 0 ? args[0] : null);
|
36
33
|
return session.infect(handler.generateNew(session, object));
|
37
34
|
}
|
38
35
|
|
@@ -42,7 +39,7 @@ public final class Generator {
|
|
42
39
|
*/
|
43
40
|
static <T extends IRubyObject> RubyString
|
44
41
|
generateJson(ThreadContext context, T object, IRubyObject[] args) {
|
45
|
-
Handler<? super T> handler = getHandlerFor(context.
|
42
|
+
Handler<? super T> handler = getHandlerFor(context.runtime, object);
|
46
43
|
return generateJson(context, object, handler, args);
|
47
44
|
}
|
48
45
|
|
@@ -54,7 +51,7 @@ public final class Generator {
|
|
54
51
|
generateJson(ThreadContext context, T object,
|
55
52
|
GeneratorState config) {
|
56
53
|
Session session = new Session(context, config);
|
57
|
-
Handler<? super T> handler = getHandlerFor(context.
|
54
|
+
Handler<? super T> handler = getHandlerFor(context.runtime, object);
|
58
55
|
return handler.generateNew(session, object);
|
59
56
|
}
|
60
57
|
|
@@ -64,18 +61,24 @@ public final class Generator {
|
|
64
61
|
// Java's generics can't handle this satisfactorily, so I'll just leave
|
65
62
|
// the best I could get and ignore the warnings
|
66
63
|
@SuppressWarnings("unchecked")
|
67
|
-
private static <T extends IRubyObject>
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
64
|
+
private static <T extends IRubyObject> Handler<? super T> getHandlerFor(Ruby runtime, T object) {
|
65
|
+
switch (((RubyBasicObject) object).getNativeClassIndex()) {
|
66
|
+
case NIL : return (Handler) NIL_HANDLER;
|
67
|
+
case TRUE : return (Handler) TRUE_HANDLER;
|
68
|
+
case FALSE : return (Handler) FALSE_HANDLER;
|
69
|
+
case FLOAT : return (Handler) FLOAT_HANDLER;
|
70
|
+
case FIXNUM : return (Handler) FIXNUM_HANDLER;
|
71
|
+
case BIGNUM : return (Handler) BIGNUM_HANDLER;
|
72
|
+
case STRING :
|
73
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break;
|
74
|
+
return (Handler) STRING_HANDLER;
|
75
|
+
case ARRAY :
|
76
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break;
|
77
|
+
return (Handler) ARRAY_HANDLER;
|
78
|
+
case HASH :
|
79
|
+
if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break;
|
80
|
+
return (Handler) HASH_HANDLER;
|
81
|
+
}
|
79
82
|
return GENERIC_HANDLER;
|
80
83
|
}
|
81
84
|
|
@@ -136,7 +139,7 @@ public final class Generator {
|
|
136
139
|
|
137
140
|
public StringEncoder getStringEncoder() {
|
138
141
|
if (stringEncoder == null) {
|
139
|
-
stringEncoder = new StringEncoder(context, getState().asciiOnly());
|
142
|
+
stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash());
|
140
143
|
}
|
141
144
|
return stringEncoder;
|
142
145
|
}
|
@@ -173,9 +176,7 @@ public final class Generator {
|
|
173
176
|
result = RubyString.newString(session.getRuntime(), buffer);
|
174
177
|
ThreadContext context = session.getContext();
|
175
178
|
RuntimeInfo info = session.getInfo();
|
176
|
-
|
177
|
-
result.force_encoding(context, info.utf8.get());
|
178
|
-
}
|
179
|
+
result.force_encoding(context, info.utf8.get());
|
179
180
|
return result;
|
180
181
|
}
|
181
182
|
|
@@ -333,13 +334,13 @@ public final class Generator {
|
|
333
334
|
|
334
335
|
buffer.append((byte)'{');
|
335
336
|
buffer.append(objectNl);
|
336
|
-
object.visitAll(new RubyHash.Visitor() {
|
337
|
-
private boolean firstPair = true;
|
338
337
|
|
338
|
+
final boolean[] firstPair = new boolean[]{true};
|
339
|
+
object.visitAll(new RubyHash.Visitor() {
|
339
340
|
@Override
|
340
341
|
public void visit(IRubyObject key, IRubyObject value) {
|
341
|
-
if (firstPair) {
|
342
|
-
firstPair = false;
|
342
|
+
if (firstPair[0]) {
|
343
|
+
firstPair[0] = false;
|
343
344
|
} else {
|
344
345
|
buffer.append((byte)',');
|
345
346
|
buffer.append(objectNl);
|
@@ -359,7 +360,7 @@ public final class Generator {
|
|
359
360
|
}
|
360
361
|
});
|
361
362
|
state.decreaseDepth();
|
362
|
-
if (objectNl.length() != 0) {
|
363
|
+
if (!firstPair[0] && objectNl.length() != 0) {
|
363
364
|
buffer.append(objectNl);
|
364
365
|
buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
|
365
366
|
}
|
@@ -382,8 +383,7 @@ public final class Generator {
|
|
382
383
|
RuntimeInfo info = session.getInfo();
|
383
384
|
RubyString src;
|
384
385
|
|
385
|
-
if (info.
|
386
|
-
object.encoding(session.getContext()) != info.utf8.get()) {
|
386
|
+
if (object.encoding(session.getContext()) != info.utf8.get()) {
|
387
387
|
src = (RubyString)object.encode(session.getContext(),
|
388
388
|
info.utf8.get());
|
389
389
|
} else {
|
@@ -428,11 +428,14 @@ public final class Generator {
|
|
428
428
|
new Handler<IRubyObject>() {
|
429
429
|
@Override
|
430
430
|
RubyString generateNew(Session session, IRubyObject object) {
|
431
|
-
|
432
|
-
object.callMethod(session.getContext(), "to_json",
|
433
|
-
|
434
|
-
|
435
|
-
|
431
|
+
if (object.respondsTo("to_json")) {
|
432
|
+
IRubyObject result = object.callMethod(session.getContext(), "to_json",
|
433
|
+
new IRubyObject[] {session.getState()});
|
434
|
+
if (result instanceof RubyString) return (RubyString)result;
|
435
|
+
throw session.getRuntime().newTypeError("to_json must return a String");
|
436
|
+
} else {
|
437
|
+
return OBJECT_HANDLER.generateNew(session, object);
|
438
|
+
}
|
436
439
|
}
|
437
440
|
|
438
441
|
@Override
|
@@ -1,8 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
|
3
3
|
*
|
4
|
-
* Distributed under the Ruby
|
5
|
-
* for details.
|
4
|
+
* Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
|
6
5
|
*/
|
7
6
|
package json.ext;
|
8
7
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
|
3
3
|
*
|
4
|
-
* Distributed under the Ruby
|
5
|
-
* for details.
|
4
|
+
* Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
|
6
5
|
*/
|
7
6
|
package json.ext;
|
8
7
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
|
3
3
|
*
|
4
|
-
* Distributed under the Ruby
|
5
|
-
* for details.
|
4
|
+
* Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
|
6
5
|
*/
|
7
6
|
package json.ext;
|
8
7
|
|
@@ -83,6 +82,12 @@ public class GeneratorState extends RubyObject {
|
|
83
82
|
*/
|
84
83
|
private boolean quirksMode = DEFAULT_QUIRKS_MODE;
|
85
84
|
static final boolean DEFAULT_QUIRKS_MODE = false;
|
85
|
+
/**
|
86
|
+
* If set to <code>true</code> the forward slash will be escaped in
|
87
|
+
* json output.
|
88
|
+
*/
|
89
|
+
private boolean escapeSlash = DEFAULT_ESCAPE_SLASH;
|
90
|
+
static final boolean DEFAULT_ESCAPE_SLASH = false;
|
86
91
|
/**
|
87
92
|
* The initial buffer length of this state. (This isn't really used on all
|
88
93
|
* non-C implementations.)
|
@@ -172,6 +177,9 @@ public class GeneratorState extends RubyObject {
|
|
172
177
|
* <code>-Infinity</code> should be generated, otherwise an exception is
|
173
178
|
* thrown if these values are encountered.
|
174
179
|
* This options defaults to <code>false</code>.
|
180
|
+
* <dt><code>:escape_slash</code>
|
181
|
+
* <dd>set to <code>true</code> if the forward slashes should be escaped
|
182
|
+
* in the json output (default: <code>false</code>)
|
175
183
|
*/
|
176
184
|
@JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
|
177
185
|
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
|
@@ -195,6 +203,7 @@ public class GeneratorState extends RubyObject {
|
|
195
203
|
this.allowNaN = orig.allowNaN;
|
196
204
|
this.asciiOnly = orig.asciiOnly;
|
197
205
|
this.quirksMode = orig.quirksMode;
|
206
|
+
this.escapeSlash = orig.escapeSlash;
|
198
207
|
this.bufferInitialLength = orig.bufferInitialLength;
|
199
208
|
this.depth = orig.depth;
|
200
209
|
return this;
|
@@ -208,45 +217,11 @@ public class GeneratorState extends RubyObject {
|
|
208
217
|
@JRubyMethod
|
209
218
|
public IRubyObject generate(ThreadContext context, IRubyObject obj) {
|
210
219
|
RubyString result = Generator.generateJson(context, obj, this);
|
211
|
-
if (!quirksMode && !objectOrArrayLiteral(result)) {
|
212
|
-
throw Utils.newException(context, Utils.M_GENERATOR_ERROR,
|
213
|
-
"only generation of JSON objects or arrays allowed");
|
214
|
-
}
|
215
220
|
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
|
216
|
-
|
217
|
-
result.force_encoding(context, info.utf8.get());
|
218
|
-
}
|
221
|
+
result.force_encoding(context, info.utf8.get());
|
219
222
|
return result;
|
220
223
|
}
|
221
224
|
|
222
|
-
/**
|
223
|
-
* Ensures the given string is in the form "[...]" or "{...}", being
|
224
|
-
* possibly surrounded by white space.
|
225
|
-
* The string's encoding must be ASCII-compatible.
|
226
|
-
* @param value
|
227
|
-
* @return
|
228
|
-
*/
|
229
|
-
private static boolean objectOrArrayLiteral(RubyString value) {
|
230
|
-
ByteList bl = value.getByteList();
|
231
|
-
int len = bl.length();
|
232
|
-
|
233
|
-
for (int pos = 0; pos < len - 1; pos++) {
|
234
|
-
int b = bl.get(pos);
|
235
|
-
if (Character.isWhitespace(b)) continue;
|
236
|
-
|
237
|
-
// match the opening brace
|
238
|
-
switch (b) {
|
239
|
-
case '[':
|
240
|
-
return matchClosingBrace(bl, pos, len, ']');
|
241
|
-
case '{':
|
242
|
-
return matchClosingBrace(bl, pos, len, '}');
|
243
|
-
default:
|
244
|
-
return false;
|
245
|
-
}
|
246
|
-
}
|
247
|
-
return false;
|
248
|
-
}
|
249
|
-
|
250
225
|
private static boolean matchClosingBrace(ByteList bl, int pos, int len,
|
251
226
|
int brace) {
|
252
227
|
for (int endPos = len - 1; endPos > pos; endPos--) {
|
@@ -381,6 +356,24 @@ public class GeneratorState extends RubyObject {
|
|
381
356
|
return max_nesting;
|
382
357
|
}
|
383
358
|
|
359
|
+
/**
|
360
|
+
* Returns true if forward slashes are escaped in the json output.
|
361
|
+
*/
|
362
|
+
public boolean escapeSlash() {
|
363
|
+
return escapeSlash;
|
364
|
+
}
|
365
|
+
|
366
|
+
@JRubyMethod(name="escape_slash")
|
367
|
+
public RubyBoolean escape_slash_get(ThreadContext context) {
|
368
|
+
return context.getRuntime().newBoolean(escapeSlash);
|
369
|
+
}
|
370
|
+
|
371
|
+
@JRubyMethod(name="escape_slash=")
|
372
|
+
public IRubyObject escape_slash_set(IRubyObject escape_slash) {
|
373
|
+
escapeSlash = escape_slash.isTrue();
|
374
|
+
return escape_slash.getRuntime().newBoolean(escapeSlash);
|
375
|
+
}
|
376
|
+
|
384
377
|
public boolean allowNaN() {
|
385
378
|
return allowNaN;
|
386
379
|
}
|
@@ -399,17 +392,6 @@ public class GeneratorState extends RubyObject {
|
|
399
392
|
return context.getRuntime().newBoolean(asciiOnly);
|
400
393
|
}
|
401
394
|
|
402
|
-
@JRubyMethod(name="quirks_mode")
|
403
|
-
public RubyBoolean quirks_mode_get(ThreadContext context) {
|
404
|
-
return context.getRuntime().newBoolean(quirksMode);
|
405
|
-
}
|
406
|
-
|
407
|
-
@JRubyMethod(name="quirks_mode=")
|
408
|
-
public IRubyObject quirks_mode_set(IRubyObject quirks_mode) {
|
409
|
-
quirksMode = quirks_mode.isTrue();
|
410
|
-
return quirks_mode.getRuntime().newBoolean(quirksMode);
|
411
|
-
}
|
412
|
-
|
413
395
|
@JRubyMethod(name="buffer_initial_length")
|
414
396
|
public RubyInteger buffer_initial_length_get(ThreadContext context) {
|
415
397
|
return context.getRuntime().newFixnum(bufferInitialLength);
|
@@ -422,11 +404,6 @@ public class GeneratorState extends RubyObject {
|
|
422
404
|
return buffer_initial_length;
|
423
405
|
}
|
424
406
|
|
425
|
-
@JRubyMethod(name="quirks_mode?")
|
426
|
-
public RubyBoolean quirks_mode_p(ThreadContext context) {
|
427
|
-
return context.getRuntime().newBoolean(quirksMode);
|
428
|
-
}
|
429
|
-
|
430
407
|
public int getDepth() {
|
431
408
|
return depth;
|
432
409
|
}
|
@@ -445,7 +422,7 @@ public class GeneratorState extends RubyObject {
|
|
445
422
|
private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
|
446
423
|
RubyString str = value.convertToString();
|
447
424
|
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
|
448
|
-
if (
|
425
|
+
if (str.encoding(context) != info.utf8.get()) {
|
449
426
|
str = (RubyString)str.encode(context, info.utf8.get());
|
450
427
|
}
|
451
428
|
return str.getByteList().dup();
|
@@ -481,7 +458,7 @@ public class GeneratorState extends RubyObject {
|
|
481
458
|
maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
|
482
459
|
allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN);
|
483
460
|
asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
|
484
|
-
|
461
|
+
escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH);
|
485
462
|
bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
|
486
463
|
|
487
464
|
depth = opts.getInt("depth", 0);
|
@@ -508,8 +485,8 @@ public class GeneratorState extends RubyObject {
|
|
508
485
|
result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context));
|
509
486
|
result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
|
510
487
|
result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
|
511
|
-
result.op_aset(context, runtime.newSymbol("quirks_mode"), quirks_mode_p(context));
|
512
488
|
result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context));
|
489
|
+
result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context));
|
513
490
|
result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
|
514
491
|
result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
|
515
492
|
for (String name: getInstanceVariableNameList()) {
|
@@ -1,8 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
|
3
3
|
*
|
4
|
-
* Distributed under the Ruby
|
5
|
-
* for details.
|
4
|
+
* Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
|
6
5
|
*/
|
7
6
|
package json.ext;
|
8
7
|
|
@@ -85,7 +84,7 @@ final class OptionsReader {
|
|
85
84
|
|
86
85
|
RubyString str = value.convertToString();
|
87
86
|
RuntimeInfo info = getRuntimeInfo();
|
88
|
-
if (
|
87
|
+
if (str.encoding(context) != info.utf8.get()) {
|
89
88
|
str = (RubyString)str.encode(context, info.utf8.get());
|
90
89
|
}
|
91
90
|
return str;
|