oj 3.16.3 β 3.16.4
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/CHANGELOG.md +4 -0
- data/ext/oj/dump.c +34 -14
- data/ext/oj/extconf.rb +1 -2
- data/ext/oj/fast.c +3 -3
- data/ext/oj/oj.c +2 -0
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parser.c +6 -3
- data/ext/oj/parser.h +2 -2
- data/ext/oj/reader.c +1 -1
- data/ext/oj/saj.c +2 -2
- data/ext/oj/usual.c +0 -27
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/version.rb +1 -1
- data/test/activesupport6/encoding_test.rb +63 -28
- data/test/activesupport7/encoding_test.rb +72 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd443226b6fbe558bd4a01e5e4e2c150149e697c22c1676007f67923b9832682
|
4
|
+
data.tar.gz: 0a46ac536c4fccaeac2bddde9ff8ef8d50343f5eeff3455b36c5bfb762aef073
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49e28b9151a1c84d9f97adcea6af90acd1141a194858f29e633bf5e2e1965bb5fab783c015e343f569692e6351ad3957059135f05eb50022b79ee799ae0a306a
|
7
|
+
data.tar.gz: 74f69e8e14c39aee7128f8b7dc23fa1fa60a8df383dec844257e67c6e7f34c58d430ac77bc06571e8d52a29f857ee0162cba8ad1e93ac8bc1f7c2ad0260a0002
|
data/CHANGELOG.md
CHANGED
data/ext/oj/dump.c
CHANGED
@@ -198,7 +198,18 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
|
198
198
|
}
|
199
199
|
|
200
200
|
inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
|
201
|
-
|
201
|
+
long size = 0;
|
202
|
+
size_t i = len;
|
203
|
+
uint8_t hi = 0;
|
204
|
+
|
205
|
+
for (; 0 < i; str++, i--) {
|
206
|
+
size += rails_friendly_chars[*str];
|
207
|
+
hi |= *str & 0x80;
|
208
|
+
}
|
209
|
+
if (0 == hi) {
|
210
|
+
return size - len * (size_t)'0';
|
211
|
+
}
|
212
|
+
return -(size - len * (size_t)'0');
|
202
213
|
}
|
203
214
|
|
204
215
|
const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
|
@@ -750,8 +761,9 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
750
761
|
void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
751
762
|
size_t size;
|
752
763
|
char *cmap;
|
753
|
-
const char *orig
|
754
|
-
bool has_hi
|
764
|
+
const char *orig = str;
|
765
|
+
bool has_hi = false;
|
766
|
+
bool do_unicode_validation = false;
|
755
767
|
|
756
768
|
switch (out->opts->escape_mode) {
|
757
769
|
case NLEsc:
|
@@ -772,8 +784,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
772
784
|
size = xss_friendly_size((uint8_t *)str, cnt);
|
773
785
|
break;
|
774
786
|
case JXEsc:
|
775
|
-
cmap
|
776
|
-
size
|
787
|
+
cmap = hixss_friendly_chars;
|
788
|
+
size = hixss_friendly_size((uint8_t *)str, cnt);
|
789
|
+
do_unicode_validation = true;
|
777
790
|
break;
|
778
791
|
case RailsXEsc: {
|
779
792
|
long sz;
|
@@ -786,12 +799,22 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
786
799
|
} else {
|
787
800
|
size = (size_t)sz;
|
788
801
|
}
|
802
|
+
do_unicode_validation = true;
|
789
803
|
break;
|
790
804
|
}
|
791
|
-
case RailsEsc:
|
805
|
+
case RailsEsc: {
|
806
|
+
long sz;
|
792
807
|
cmap = rails_friendly_chars;
|
793
|
-
|
808
|
+
sz = rails_friendly_size((uint8_t *)str, cnt);
|
809
|
+
if (sz < 0) {
|
810
|
+
has_hi = true;
|
811
|
+
size = (size_t)-sz;
|
812
|
+
} else {
|
813
|
+
size = (size_t)sz;
|
814
|
+
}
|
815
|
+
do_unicode_validation = true;
|
794
816
|
break;
|
817
|
+
}
|
795
818
|
case JSONEsc:
|
796
819
|
default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
|
797
820
|
}
|
@@ -822,7 +845,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
822
845
|
for (; str < end; str++) {
|
823
846
|
switch (cmap[(uint8_t)*str]) {
|
824
847
|
case '1':
|
825
|
-
if (
|
848
|
+
if (do_unicode_validation && check_start <= str) {
|
826
849
|
if (0 != (0x80 & (uint8_t)*str)) {
|
827
850
|
if (0xC0 == (0xC0 & (uint8_t)*str)) {
|
828
851
|
check_start = check_unicode(str, end, orig);
|
@@ -846,8 +869,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
846
869
|
}
|
847
870
|
break;
|
848
871
|
case '3': // Unicode
|
849
|
-
if (0xe2 == (uint8_t)*str &&
|
850
|
-
2 <= end - str) {
|
872
|
+
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
851
873
|
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
852
874
|
str = dump_unicode(str, end, out, orig);
|
853
875
|
} else {
|
@@ -866,8 +888,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
866
888
|
APPEND_CHARS(out->cur, "\\u00", 4);
|
867
889
|
dump_hex((uint8_t)*str, out);
|
868
890
|
} else {
|
869
|
-
if (0xe2 == (uint8_t)*str &&
|
870
|
-
(JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
|
891
|
+
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
871
892
|
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
872
893
|
str = dump_unicode(str, end, out, orig);
|
873
894
|
} else {
|
@@ -884,8 +905,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
884
905
|
}
|
885
906
|
*out->cur++ = '"';
|
886
907
|
}
|
887
|
-
if (
|
888
|
-
0 != (0x80 & *(str - 1))) {
|
908
|
+
if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
|
889
909
|
uint8_t c = (uint8_t) * (str - 1);
|
890
910
|
int i;
|
891
911
|
int scnt = (int)(str - orig);
|
data/ext/oj/extconf.rb
CHANGED
@@ -29,10 +29,9 @@ dflags = {
|
|
29
29
|
have_func('rb_gc_mark_movable')
|
30
30
|
have_func('stpcpy')
|
31
31
|
have_func('pthread_mutex_init')
|
32
|
+
have_func('getrlimit', 'sys/resource.h')
|
32
33
|
have_func('rb_enc_interned_str')
|
33
34
|
have_func('rb_ext_ractor_safe', 'ruby.h')
|
34
|
-
# rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
|
35
|
-
have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
|
36
35
|
|
37
36
|
dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
|
38
37
|
|
data/ext/oj/fast.c
CHANGED
@@ -40,7 +40,7 @@ typedef struct _doc {
|
|
40
40
|
Leaf *where; // points to current location
|
41
41
|
Leaf where_path[MAX_STACK]; // points to head of path
|
42
42
|
char *json;
|
43
|
-
unsigned long size;
|
43
|
+
unsigned long size; // number of leaves/branches in the doc
|
44
44
|
VALUE self;
|
45
45
|
Batch batches;
|
46
46
|
struct _batch batch0;
|
@@ -573,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
|
|
573
573
|
char *h = pi->s; // head
|
574
574
|
char *t = h; // tail
|
575
575
|
|
576
|
-
h++;
|
576
|
+
h++; // skip quote character
|
577
577
|
t++;
|
578
578
|
value = h;
|
579
579
|
for (; '"' != *h; h++, t++) {
|
@@ -765,7 +765,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
|
|
765
765
|
pi.s = pi.str;
|
766
766
|
doc_init(doc);
|
767
767
|
pi.doc = doc;
|
768
|
-
#if IS_WINDOWS
|
768
|
+
#if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
|
769
769
|
// assume a 1M stack and give half to ruby
|
770
770
|
pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
|
771
771
|
#else
|
data/ext/oj/oj.c
CHANGED
@@ -36,6 +36,7 @@ ID oj_as_json_id;
|
|
36
36
|
ID oj_begin_id;
|
37
37
|
ID oj_bigdecimal_id;
|
38
38
|
ID oj_end_id;
|
39
|
+
ID oj_eofq_id;
|
39
40
|
ID oj_exclude_end_id;
|
40
41
|
ID oj_error_id;
|
41
42
|
ID oj_file_id;
|
@@ -1849,6 +1850,7 @@ void Init_oj(void) {
|
|
1849
1850
|
oj_begin_id = rb_intern("begin");
|
1850
1851
|
oj_bigdecimal_id = rb_intern("BigDecimal");
|
1851
1852
|
oj_end_id = rb_intern("end");
|
1853
|
+
oj_eofq_id = rb_intern("eof?");
|
1852
1854
|
oj_error_id = rb_intern("error");
|
1853
1855
|
oj_exclude_end_id = rb_intern("exclude_end?");
|
1854
1856
|
oj_file_id = rb_intern("file?");
|
data/ext/oj/oj.h
CHANGED
data/ext/oj/parser.c
CHANGED
@@ -1114,9 +1114,6 @@ static void parse(ojParser p, const byte *json) {
|
|
1114
1114
|
p->map = trail_map;
|
1115
1115
|
}
|
1116
1116
|
}
|
1117
|
-
if (0 < p->depth) {
|
1118
|
-
parse_error(p, "parse error, not closed");
|
1119
|
-
}
|
1120
1117
|
if (0 == p->depth) {
|
1121
1118
|
switch (p->map[256]) {
|
1122
1119
|
case '0':
|
@@ -1394,6 +1391,12 @@ static VALUE load(VALUE self) {
|
|
1394
1391
|
if (0 < RSTRING_LEN(rbuf)) {
|
1395
1392
|
parse(p, (byte *)StringValuePtr(rbuf));
|
1396
1393
|
}
|
1394
|
+
if (Qtrue == rb_funcall(p->reader, oj_eofq_id, 0)) {
|
1395
|
+
if (0 < p->depth) {
|
1396
|
+
parse_error(p, "parse error, not closed");
|
1397
|
+
}
|
1398
|
+
break;
|
1399
|
+
}
|
1397
1400
|
}
|
1398
1401
|
return Qtrue;
|
1399
1402
|
}
|
data/ext/oj/parser.h
CHANGED
@@ -32,9 +32,9 @@ typedef struct _num {
|
|
32
32
|
long double dub;
|
33
33
|
int64_t fixnum; // holds all digits
|
34
34
|
uint32_t len;
|
35
|
-
int16_t div;
|
35
|
+
int16_t div; // 10^div
|
36
36
|
int16_t exp;
|
37
|
-
uint8_t shift;
|
37
|
+
uint8_t shift; // shift of fixnum to get decimal
|
38
38
|
bool neg;
|
39
39
|
bool exp_neg;
|
40
40
|
// for numbers as strings, reuse buf
|
data/ext/oj/reader.c
CHANGED
@@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) {
|
|
101
101
|
} else {
|
102
102
|
shift = reader->pro - reader->head - 1; // leave one character so we can backup one
|
103
103
|
}
|
104
|
-
if (0 >= shift) {
|
104
|
+
if (0 >= shift) { /* no space left so allocate more */
|
105
105
|
const char *old = reader->head;
|
106
106
|
size_t size = reader->end - reader->head + BUF_PAD;
|
107
107
|
|
data/ext/oj/saj.c
CHANGED
@@ -578,7 +578,7 @@ static void saj_parse(VALUE handler, char *json) {
|
|
578
578
|
/* initialize parse info */
|
579
579
|
pi.str = json;
|
580
580
|
pi.s = json;
|
581
|
-
#if IS_WINDOWS
|
581
|
+
#if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
|
582
582
|
pi.stack_min = (void *)((char *)&obj - (512L * 1024L)); /* assume a 1M stack and give half to ruby */
|
583
583
|
#else
|
584
584
|
{
|
@@ -587,7 +587,7 @@ static void saj_parse(VALUE handler, char *json) {
|
|
587
587
|
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
588
588
|
pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 * 3)); /* let 3/4ths of the stack be used only */
|
589
589
|
} else {
|
590
|
-
pi.stack_min = 0;
|
590
|
+
pi.stack_min = 0; /* indicates not to check stack limit */
|
591
591
|
}
|
592
592
|
}
|
593
593
|
#endif
|
data/ext/oj/usual.c
CHANGED
@@ -281,7 +281,6 @@ static void close_object(ojParser p) {
|
|
281
281
|
VALUE *head = d->vhead + c->vi + 1;
|
282
282
|
volatile VALUE obj = rb_hash_new();
|
283
283
|
|
284
|
-
#if HAVE_RB_HASH_BULK_INSERT
|
285
284
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
286
285
|
*vp = d->get_key(p, kp);
|
287
286
|
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
@@ -289,14 +288,6 @@ static void close_object(ojParser p) {
|
|
289
288
|
}
|
290
289
|
}
|
291
290
|
rb_hash_bulk_insert(d->vtail - head, head, obj);
|
292
|
-
#else
|
293
|
-
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
294
|
-
rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
|
295
|
-
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
296
|
-
OJ_R_FREE(kp->key);
|
297
|
-
}
|
298
|
-
}
|
299
|
-
#endif
|
300
291
|
d->ktail = d->khead + c->ki;
|
301
292
|
d->vtail = head;
|
302
293
|
head--;
|
@@ -341,7 +332,6 @@ static void close_object_create(ojParser p) {
|
|
341
332
|
head++;
|
342
333
|
if (Qnil == d->hash_class) {
|
343
334
|
obj = rb_hash_new();
|
344
|
-
#if HAVE_RB_HASH_BULK_INSERT
|
345
335
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
346
336
|
*vp = d->get_key(p, kp);
|
347
337
|
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
@@ -349,14 +339,6 @@ static void close_object_create(ojParser p) {
|
|
349
339
|
}
|
350
340
|
}
|
351
341
|
rb_hash_bulk_insert(d->vtail - head, head, obj);
|
352
|
-
#else
|
353
|
-
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
354
|
-
rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
|
355
|
-
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
356
|
-
OJ_R_FREE(kp->key);
|
357
|
-
}
|
358
|
-
}
|
359
|
-
#endif
|
360
342
|
} else {
|
361
343
|
obj = rb_class_new_instance(0, NULL, d->hash_class);
|
362
344
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
@@ -373,7 +355,6 @@ static void close_object_create(ojParser p) {
|
|
373
355
|
if (!d->ignore_json_create && rb_respond_to(clas, oj_json_create_id)) {
|
374
356
|
volatile VALUE arg = rb_hash_new();
|
375
357
|
|
376
|
-
#if HAVE_RB_HASH_BULK_INSERT
|
377
358
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
378
359
|
*vp = d->get_key(p, kp);
|
379
360
|
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
@@ -381,14 +362,6 @@ static void close_object_create(ojParser p) {
|
|
381
362
|
}
|
382
363
|
}
|
383
364
|
rb_hash_bulk_insert(d->vtail - head, head, arg);
|
384
|
-
#else
|
385
|
-
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
386
|
-
rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
|
387
|
-
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
388
|
-
OJ_R_FREE(kp->key);
|
389
|
-
}
|
390
|
-
}
|
391
|
-
#endif
|
392
365
|
obj = rb_funcall(clas, oj_json_create_id, 1, arg);
|
393
366
|
} else {
|
394
367
|
obj = rb_class_new_instance(0, NULL, clas);
|
data/lib/oj/schandler.rb
CHANGED
@@ -64,13 +64,14 @@ module Oj
|
|
64
64
|
#
|
65
65
|
# hash_end
|
66
66
|
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# the key-value pair that follows.
|
67
|
+
# At the end of a JSON object element the hash_end() callback is called if
|
68
|
+
# public.
|
70
69
|
#
|
71
70
|
# hash_key
|
72
71
|
#
|
73
|
-
#
|
72
|
+
# When a hash key is encountered the hash_key() method is called with the
|
73
|
+
# parsed hash value key. The return value from the call is then used as the
|
74
|
+
# key in the key-value pair that follows.
|
74
75
|
#
|
75
76
|
# hash_set
|
76
77
|
#
|
data/lib/oj/version.rb
CHANGED
@@ -74,42 +74,77 @@ class TestJSONEncoding < ActiveSupport::TestCase
|
|
74
74
|
ActiveSupport.escape_html_entities_in_json = false
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
78
|
-
|
79
|
-
# ActiveSupport.escape_html_entities_in_json reverts to true even after
|
80
|
-
# being set to false. I haven't been able to figure that out so the value is
|
81
|
-
# set to true, the default, before running the test. This might be wrong but
|
82
|
-
# for now it will have to do.
|
83
|
-
ActiveSupport.escape_html_entities_in_json = true
|
84
|
-
result = ActiveSupport::JSON.encode("β¬2.99")
|
85
|
-
assert_equal '"β¬2.99"', result
|
86
|
-
assert_equal(Encoding::UTF_8, result.encoding)
|
87
|
-
|
88
|
-
result = ActiveSupport::JSON.encode("ββΊ")
|
89
|
-
assert_equal '"ββΊ"', result
|
90
|
-
assert_equal(Encoding::UTF_8, result.encoding)
|
77
|
+
def test_hash_keys_encoding_without_escaping
|
78
|
+
assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
|
91
79
|
end
|
92
80
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
81
|
+
module UnicodeTests
|
82
|
+
def test_utf8_string_encoded_properly
|
83
|
+
result = ActiveSupport::JSON.encode("β¬2.99")
|
84
|
+
assert_equal '"β¬2.99"', result
|
85
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
86
|
+
|
87
|
+
result = ActiveSupport::JSON.encode("ββΊ")
|
88
|
+
assert_equal '"ββΊ"', result
|
89
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_non_utf8_string_transcodes
|
93
|
+
s = "δΊ".encode("Shift_JIS")
|
94
|
+
result = ActiveSupport::JSON.encode(s)
|
95
|
+
assert_equal '"δΊ"', result
|
96
|
+
assert_equal Encoding::UTF_8, result.encoding
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_wide_utf8_chars
|
100
|
+
w = "π "
|
101
|
+
result = ActiveSupport::JSON.encode(w)
|
102
|
+
assert_equal '"π "', result
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_wide_utf8_roundtrip
|
106
|
+
hash = { string: "π" }
|
107
|
+
json = ActiveSupport::JSON.encode(hash)
|
108
|
+
decoded_hash = ActiveSupport::JSON.decode(json)
|
109
|
+
assert_equal "π", decoded_hash["string"]
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_invalid_encoding_raises
|
113
|
+
s = "\xAE\xFF\x9F"
|
114
|
+
refute s.valid_encoding?
|
115
|
+
|
116
|
+
# n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
|
117
|
+
# if you do that (even indirectly through Oj.optimize_rails), then this raises a
|
118
|
+
# JSON::GeneratorError instead of an EncodingError.
|
119
|
+
assert_raises(EncodingError) do
|
120
|
+
ActiveSupport::JSON.encode([s])
|
121
|
+
end
|
122
|
+
end
|
98
123
|
end
|
99
124
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
125
|
+
module UnicodeTestsWithEscapingOn
|
126
|
+
def setup
|
127
|
+
ActiveSupport.escape_html_entities_in_json = true
|
128
|
+
end
|
129
|
+
|
130
|
+
def teardown
|
131
|
+
ActiveSupport.escape_html_entities_in_json = false
|
132
|
+
end
|
133
|
+
|
134
|
+
include UnicodeTests
|
104
135
|
end
|
105
136
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
137
|
+
module UnicodeTestsWithEscapingOff
|
138
|
+
def setup
|
139
|
+
ActiveSupport.escape_html_entities_in_json = false
|
140
|
+
end
|
141
|
+
|
142
|
+
include UnicodeTests
|
111
143
|
end
|
112
144
|
|
145
|
+
include UnicodeTestsWithEscapingOn
|
146
|
+
include UnicodeTestsWithEscapingOff
|
147
|
+
|
113
148
|
def test_hash_key_identifiers_are_always_quoted
|
114
149
|
values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
|
115
150
|
assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
|
@@ -8,9 +8,18 @@ require "active_support/time"
|
|
8
8
|
require_relative "time_zone_test_helpers"
|
9
9
|
require_relative "encoding_test_cases"
|
10
10
|
|
11
|
+
require 'oj'
|
12
|
+
# Sets the ActiveSupport encoder to be Oj and also wraps the setting of globals.
|
13
|
+
Oj::Rails.set_encoder()
|
14
|
+
Oj::Rails.optimize()
|
15
|
+
|
11
16
|
class TestJSONEncoding < ActiveSupport::TestCase
|
12
17
|
include TimeZoneTestHelpers
|
13
18
|
|
19
|
+
def test_is_actually_oj
|
20
|
+
assert_equal Oj::Rails::Encoder, ActiveSupport.json_encoder
|
21
|
+
end
|
22
|
+
|
14
23
|
def sorted_json(json)
|
15
24
|
if json.start_with?("{") && json.end_with?("}")
|
16
25
|
"{" + json[1..-2].split(",").sort.join(",") + "}"
|
@@ -61,36 +70,77 @@ class TestJSONEncoding < ActiveSupport::TestCase
|
|
61
70
|
ActiveSupport.escape_html_entities_in_json = false
|
62
71
|
end
|
63
72
|
|
64
|
-
def
|
65
|
-
|
66
|
-
assert_equal '"β¬2.99"', result
|
67
|
-
assert_equal(Encoding::UTF_8, result.encoding)
|
68
|
-
|
69
|
-
result = ActiveSupport::JSON.encode("ββΊ")
|
70
|
-
assert_equal '"ββΊ"', result
|
71
|
-
assert_equal(Encoding::UTF_8, result.encoding)
|
73
|
+
def test_hash_keys_encoding_without_escaping
|
74
|
+
assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
|
72
75
|
end
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
module UnicodeTests
|
78
|
+
def test_utf8_string_encoded_properly
|
79
|
+
result = ActiveSupport::JSON.encode("β¬2.99")
|
80
|
+
assert_equal '"β¬2.99"', result
|
81
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
82
|
+
|
83
|
+
result = ActiveSupport::JSON.encode("ββΊ")
|
84
|
+
assert_equal '"ββΊ"', result
|
85
|
+
assert_equal(Encoding::UTF_8, result.encoding)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_non_utf8_string_transcodes
|
89
|
+
s = "δΊ".encode("Shift_JIS")
|
90
|
+
result = ActiveSupport::JSON.encode(s)
|
91
|
+
assert_equal '"δΊ"', result
|
92
|
+
assert_equal Encoding::UTF_8, result.encoding
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_wide_utf8_chars
|
96
|
+
w = "π "
|
97
|
+
result = ActiveSupport::JSON.encode(w)
|
98
|
+
assert_equal '"π "', result
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_wide_utf8_roundtrip
|
102
|
+
hash = { string: "π" }
|
103
|
+
json = ActiveSupport::JSON.encode(hash)
|
104
|
+
decoded_hash = ActiveSupport::JSON.decode(json)
|
105
|
+
assert_equal "π", decoded_hash["string"]
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_invalid_encoding_raises
|
109
|
+
s = "\xAE\xFF\x9F"
|
110
|
+
refute s.valid_encoding?
|
111
|
+
|
112
|
+
# n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
|
113
|
+
# if you do that (even indirectly through Oj.optimize_rails), then this raises a
|
114
|
+
# JSON::GeneratorError instead of an EncodingError.
|
115
|
+
assert_raises(EncodingError) do
|
116
|
+
ActiveSupport::JSON.encode([s])
|
117
|
+
end
|
118
|
+
end
|
79
119
|
end
|
80
120
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
121
|
+
module UnicodeTestsWithEscapingOn
|
122
|
+
def setup
|
123
|
+
ActiveSupport.escape_html_entities_in_json = true
|
124
|
+
end
|
125
|
+
|
126
|
+
def teardown
|
127
|
+
ActiveSupport.escape_html_entities_in_json = false
|
128
|
+
end
|
129
|
+
|
130
|
+
include UnicodeTests
|
85
131
|
end
|
86
132
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
133
|
+
module UnicodeTestsWithEscapingOff
|
134
|
+
def setup
|
135
|
+
ActiveSupport.escape_html_entities_in_json = false
|
136
|
+
end
|
137
|
+
|
138
|
+
include UnicodeTests
|
92
139
|
end
|
93
140
|
|
141
|
+
include UnicodeTestsWithEscapingOn
|
142
|
+
include UnicodeTestsWithEscapingOff
|
143
|
+
|
94
144
|
def test_hash_key_identifiers_are_always_quoted
|
95
145
|
values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
|
96
146
|
assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.16.
|
4
|
+
version: 3.16.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|