oj 3.16.1 → 3.16.7
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 +28 -0
- data/ext/oj/cache.c +4 -2
- data/ext/oj/cache.h +3 -2
- data/ext/oj/dump.c +39 -16
- data/ext/oj/extconf.rb +1 -2
- data/ext/oj/fast.c +10 -9
- data/ext/oj/intern.c +23 -6
- data/ext/oj/mimic_json.c +11 -5
- data/ext/oj/object.c +13 -5
- data/ext/oj/oj.c +131 -130
- data/ext/oj/oj.h +4 -3
- data/ext/oj/parser.c +67 -53
- data/ext/oj/parser.h +2 -2
- data/ext/oj/rails.c +25 -6
- data/ext/oj/reader.c +1 -1
- data/ext/oj/saj.c +2 -2
- data/ext/oj/stream_writer.c +35 -15
- data/ext/oj/string_writer.c +54 -20
- data/ext/oj/usual.c +37 -28
- data/ext/oj/usual.h +1 -0
- data/ext/oj/val_stack.c +13 -2
- data/lib/oj/mimic.rb +1 -5
- 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/abstract_unit.rb +4 -1
- data/test/activesupport7/encoding_test.rb +72 -22
- data/test/foo.rb +16 -10
- data/test/json_gem/json_generator_test.rb +6 -0
- data/test/perf_dump.rb +1 -1
- data/test/prec.rb +4 -4
- data/test/test_compat.rb +1 -1
- data/test/test_custom.rb +2 -1
- data/test/test_object.rb +14 -0
- data/test/test_parser_debug.rb +1 -1
- data/test/test_parser_usual.rb +34 -0
- metadata +31 -3
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,18 +288,15 @@ 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;
|
|
292
|
+
|
|
301
293
|
d->vtail = head;
|
|
302
294
|
head--;
|
|
303
295
|
*head = obj;
|
|
296
|
+
if (1 == d->vtail - d->vhead && rb_block_given_p()) {
|
|
297
|
+
d->vtail = d->vhead;
|
|
298
|
+
rb_yield(obj);
|
|
299
|
+
}
|
|
304
300
|
}
|
|
305
301
|
|
|
306
302
|
static void close_object_class(ojParser p) {
|
|
@@ -341,7 +337,6 @@ static void close_object_create(ojParser p) {
|
|
|
341
337
|
head++;
|
|
342
338
|
if (Qnil == d->hash_class) {
|
|
343
339
|
obj = rb_hash_new();
|
|
344
|
-
#if HAVE_RB_HASH_BULK_INSERT
|
|
345
340
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
|
346
341
|
*vp = d->get_key(p, kp);
|
|
347
342
|
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
|
@@ -349,14 +344,6 @@ static void close_object_create(ojParser p) {
|
|
|
349
344
|
}
|
|
350
345
|
}
|
|
351
346
|
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
347
|
} else {
|
|
361
348
|
obj = rb_class_new_instance(0, NULL, d->hash_class);
|
|
362
349
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
|
@@ -373,7 +360,6 @@ static void close_object_create(ojParser p) {
|
|
|
373
360
|
if (!d->ignore_json_create && rb_respond_to(clas, oj_json_create_id)) {
|
|
374
361
|
volatile VALUE arg = rb_hash_new();
|
|
375
362
|
|
|
376
|
-
#if HAVE_RB_HASH_BULK_INSERT
|
|
377
363
|
for (vp = head; kp < d->ktail; kp++, vp += 2) {
|
|
378
364
|
*vp = d->get_key(p, kp);
|
|
379
365
|
if (sizeof(kp->buf) <= (size_t)kp->len) {
|
|
@@ -381,14 +367,6 @@ static void close_object_create(ojParser p) {
|
|
|
381
367
|
}
|
|
382
368
|
}
|
|
383
369
|
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
370
|
obj = rb_funcall(clas, oj_json_create_id, 1, arg);
|
|
393
371
|
} else {
|
|
394
372
|
obj = rb_class_new_instance(0, NULL, clas);
|
|
@@ -599,7 +577,21 @@ static VALUE result(ojParser p) {
|
|
|
599
577
|
Usual d = (Usual)p->ctx;
|
|
600
578
|
|
|
601
579
|
if (d->vhead < d->vtail) {
|
|
602
|
-
|
|
580
|
+
long cnt = d->vtail - d->vhead;
|
|
581
|
+
volatile VALUE ary;
|
|
582
|
+
volatile VALUE *vp;
|
|
583
|
+
|
|
584
|
+
if (1 == cnt) {
|
|
585
|
+
return *d->vhead;
|
|
586
|
+
}
|
|
587
|
+
ary = rb_ary_new();
|
|
588
|
+
for (vp = d->vhead; vp < d->vtail; vp++) {
|
|
589
|
+
rb_ary_push(ary, *vp);
|
|
590
|
+
}
|
|
591
|
+
return ary;
|
|
592
|
+
}
|
|
593
|
+
if (d->raise_on_empty) {
|
|
594
|
+
rb_raise(oj_parse_error_class, "empty string");
|
|
603
595
|
}
|
|
604
596
|
return Qnil;
|
|
605
597
|
}
|
|
@@ -1066,6 +1058,20 @@ static VALUE opt_symbol_keys_set(ojParser p, VALUE value) {
|
|
|
1066
1058
|
return (NULL != d->sym_cache) ? Qtrue : Qfalse;
|
|
1067
1059
|
}
|
|
1068
1060
|
|
|
1061
|
+
static VALUE opt_raise_on_empty(ojParser p, VALUE value) {
|
|
1062
|
+
Usual d = (Usual)p->ctx;
|
|
1063
|
+
|
|
1064
|
+
return d->raise_on_empty ? Qtrue : Qfalse;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
static VALUE opt_raise_on_empty_set(ojParser p, VALUE value) {
|
|
1068
|
+
Usual d = (Usual)p->ctx;
|
|
1069
|
+
|
|
1070
|
+
d->raise_on_empty = (Qtrue == value);
|
|
1071
|
+
|
|
1072
|
+
return d->raise_on_empty ? Qtrue : Qfalse;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1069
1075
|
static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
1070
1076
|
struct opt *op;
|
|
1071
1077
|
struct opt opts[] = {
|
|
@@ -1095,6 +1101,8 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
1095
1101
|
{.name = "omit_null=", .func = opt_omit_null_set},
|
|
1096
1102
|
{.name = "symbol_keys", .func = opt_symbol_keys},
|
|
1097
1103
|
{.name = "symbol_keys=", .func = opt_symbol_keys_set},
|
|
1104
|
+
{.name = "raise_on_empty", .func = opt_raise_on_empty},
|
|
1105
|
+
{.name = "raise_on_empty=", .func = opt_raise_on_empty_set},
|
|
1098
1106
|
{.name = NULL},
|
|
1099
1107
|
};
|
|
1100
1108
|
|
|
@@ -1129,6 +1137,7 @@ void oj_init_usual(ojParser p, Usual d) {
|
|
|
1129
1137
|
d->get_key = cache_key;
|
|
1130
1138
|
d->cache_keys = true;
|
|
1131
1139
|
d->ignore_json_create = false;
|
|
1140
|
+
d->raise_on_empty = false;
|
|
1132
1141
|
d->cache_str = 6;
|
|
1133
1142
|
d->array_class = Qnil;
|
|
1134
1143
|
d->hash_class = Qnil;
|
data/ext/oj/usual.h
CHANGED
data/ext/oj/val_stack.c
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
#include "odd.h"
|
|
9
9
|
#include "oj.h"
|
|
10
10
|
|
|
11
|
-
static void
|
|
11
|
+
static void stack_mark(void *ptr) {
|
|
12
12
|
ValStack stack = (ValStack)ptr;
|
|
13
13
|
Val v;
|
|
14
14
|
|
|
@@ -46,6 +46,17 @@ static void mark(void *ptr) {
|
|
|
46
46
|
#endif
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
static const rb_data_type_t oj_stack_type = {
|
|
50
|
+
"Oj/stack",
|
|
51
|
+
{
|
|
52
|
+
stack_mark,
|
|
53
|
+
NULL,
|
|
54
|
+
NULL,
|
|
55
|
+
},
|
|
56
|
+
0,
|
|
57
|
+
0,
|
|
58
|
+
};
|
|
59
|
+
|
|
49
60
|
VALUE
|
|
50
61
|
oj_stack_init(ValStack stack) {
|
|
51
62
|
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
@@ -70,7 +81,7 @@ oj_stack_init(ValStack stack) {
|
|
|
70
81
|
stack->head->clen = 0;
|
|
71
82
|
stack->head->next = NEXT_NONE;
|
|
72
83
|
|
|
73
|
-
return
|
|
84
|
+
return TypedData_Wrap_Struct(oj_cstack_class, &oj_stack_type, stack);
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
const char *oj_stack_next_string(ValNext n) {
|
data/lib/oj/mimic.rb
CHANGED
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))
|
|
@@ -19,7 +19,10 @@ require "active_support"
|
|
|
19
19
|
Thread.abort_on_exception = true
|
|
20
20
|
|
|
21
21
|
# Show backtraces for deprecated behavior for quicker cleanup.
|
|
22
|
-
ActiveSupport::Deprecation.debug
|
|
22
|
+
if ActiveSupport::Deprecation.respond_to?(:debug)
|
|
23
|
+
# Rails 7.2 does not have ActiveSupport::Deprecation.debug
|
|
24
|
+
ActiveSupport::Deprecation.debug = true
|
|
25
|
+
end
|
|
23
26
|
|
|
24
27
|
# Default to old to_time behavior but allow running tests with new behavior
|
|
25
28
|
ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
|
|
@@ -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))
|
data/test/foo.rb
CHANGED
|
@@ -5,16 +5,22 @@ $LOAD_PATH << '.'
|
|
|
5
5
|
$LOAD_PATH << File.join(__dir__, '../lib')
|
|
6
6
|
$LOAD_PATH << File.join(__dir__, '../ext')
|
|
7
7
|
|
|
8
|
-
require 'active_support'
|
|
9
|
-
require 'json'
|
|
10
8
|
require 'oj'
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
# Oj::Rails.mimic_JSON()
|
|
10
|
+
reader, writer = IO.pipe
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
end
|
|
12
|
+
thread =
|
|
13
|
+
Thread.new do
|
|
14
|
+
5.times do |id|
|
|
15
|
+
Oj.to_stream(writer, { "id" => id })
|
|
16
|
+
sleep(1)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
writer.close
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
p = Oj::Parser.new(:usual)
|
|
23
|
+
p.load(reader) { |data| puts "#{Time.now} -- ID: #{data["id"]}" }
|
|
24
|
+
|
|
25
|
+
reader.close
|
|
26
|
+
thread.join
|
|
@@ -142,6 +142,8 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
|
142
142
|
# seems to occur on travis but not locally.
|
|
143
143
|
actual = state.to_h
|
|
144
144
|
actual.delete(:escape_slash)
|
|
145
|
+
actual.delete(:strict)
|
|
146
|
+
actual.delete(:script_safe)
|
|
145
147
|
assert_equal({
|
|
146
148
|
:allow_nan => false,
|
|
147
149
|
:array_nl => "\n",
|
|
@@ -162,6 +164,8 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
|
162
164
|
# seems to occur on travis but not locally.
|
|
163
165
|
actual = state.to_h
|
|
164
166
|
actual.delete(:escape_slash)
|
|
167
|
+
actual.delete(:strict)
|
|
168
|
+
actual.delete(:script_safe)
|
|
165
169
|
assert_equal({
|
|
166
170
|
:allow_nan => false,
|
|
167
171
|
:array_nl => "",
|
|
@@ -182,6 +186,8 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
|
182
186
|
# seems to occur on travis but not locally.
|
|
183
187
|
actual = state.to_h
|
|
184
188
|
actual.delete(:escape_slash)
|
|
189
|
+
actual.delete(:strict)
|
|
190
|
+
actual.delete(:script_safe)
|
|
185
191
|
assert_equal({
|
|
186
192
|
:allow_nan => false,
|
|
187
193
|
:array_nl => "",
|
data/test/perf_dump.rb
CHANGED
|
@@ -28,7 +28,7 @@ opts.parse(ARGV)
|
|
|
28
28
|
'd' => [ true, [false, [-123_456_789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
|
|
29
29
|
'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
|
|
30
30
|
'f' => nil, # nil
|
|
31
|
-
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
|
31
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => { 'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
|
32
32
|
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
|
33
33
|
}
|
|
34
34
|
|
data/test/prec.rb
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
require 'oj'
|
|
4
4
|
|
|
5
|
-
extras = {'locationLng' => -97.14690769100295}
|
|
5
|
+
extras = { 'locationLng' => -97.14690769100295 }
|
|
6
6
|
|
|
7
|
-
Oj.default_options = {float_precision: 17}
|
|
7
|
+
Oj.default_options = { float_precision: 17 }
|
|
8
8
|
|
|
9
9
|
encoded = Oj.dump(extras)
|
|
10
10
|
puts encoded
|
|
@@ -15,8 +15,8 @@ require 'active_record'
|
|
|
15
15
|
Oj::Rails.set_encoder()
|
|
16
16
|
Oj::Rails.set_decoder()
|
|
17
17
|
|
|
18
|
-
Oj.default_options = {float_precision: 17}
|
|
19
|
-
# Using Oj rails encoder, gets the correct value: {'locationLng':-97.14690769100295}
|
|
18
|
+
Oj.default_options = { float_precision: 17 }
|
|
19
|
+
# Using Oj rails encoder, gets the correct value: { 'locationLng':-97.14690769100295 }
|
|
20
20
|
encoded = ActiveSupport::JSON.encode(extras)
|
|
21
21
|
puts encoded
|
|
22
22
|
puts ActiveSupport::JSON.decode(encoded)
|
data/test/test_compat.rb
CHANGED
|
@@ -468,7 +468,7 @@ class CompatJuice < Minitest::Test
|
|
|
468
468
|
|
|
469
469
|
def test_arg_passing
|
|
470
470
|
json = Oj.to_json(Argy.new(), :max_nesting => 40)
|
|
471
|
-
|
|
471
|
+
assert_match(/.*max_nesting.*40.*/, json)
|
|
472
472
|
end
|
|
473
473
|
|
|
474
474
|
def test_max_nesting
|
data/test/test_custom.rb
CHANGED
|
@@ -503,8 +503,9 @@ class CustomJuice < Minitest::Test
|
|
|
503
503
|
# These two forms will lose precision while dumping as they don't
|
|
504
504
|
# preserve full precision. We check that a dumped version is equal
|
|
505
505
|
# to that version loaded and dumped a second time, but don't check
|
|
506
|
-
# that the loaded Ruby
|
|
506
|
+
# that the loaded Ruby object is still the same as the original.
|
|
507
507
|
dump_load_dump(obj, false, :time_format => :xmlschema, :create_id => '^o', :create_additions => true)
|
|
508
|
+
dump_load_dump(obj, false, :time_format => :xmlschema, :create_id => '^o', :create_additions => true, second_precision: 3)
|
|
508
509
|
dump_load_dump(obj, false, :time_format => :ruby, :create_id => '^o', :create_additions => true)
|
|
509
510
|
end
|
|
510
511
|
|
data/test/test_object.rb
CHANGED
|
@@ -951,6 +951,20 @@ class ObjectJuice < Minitest::Test
|
|
|
951
951
|
dump_and_load(DateTime.new(2012, 6, 19, 13, 5, Rational(7_123_456_789, 1_000_000_000)), false)
|
|
952
952
|
end
|
|
953
953
|
|
|
954
|
+
def test_odd_xml_time
|
|
955
|
+
str = "2023-01-01T00:00:00Z"
|
|
956
|
+
assert_equal(Time.parse(str), Oj.load('{"^t":"' + str + '"}', mode: :object))
|
|
957
|
+
|
|
958
|
+
str = "2023-01-01T00:00:00.3Z"
|
|
959
|
+
assert_equal(Time.parse(str), Oj.load('{"^t":"' + str + '"}', mode: :object))
|
|
960
|
+
|
|
961
|
+
str = "2023-01-01T00:00:00.123456789123456789Z"
|
|
962
|
+
assert_equal(Time.parse(str), Oj.load('{"^t":"' + str + '"}', mode: :object))
|
|
963
|
+
|
|
964
|
+
str = "2023-01-01T00:00:00.123456789123456789123456789Z"
|
|
965
|
+
assert_equal(Time.parse(str), Oj.load('{"^t":"' + str + '"}', mode: :object))
|
|
966
|
+
end
|
|
967
|
+
|
|
954
968
|
def test_bag
|
|
955
969
|
json = %{{
|
|
956
970
|
"^o":"ObjectJuice::Jem",
|
data/test/test_parser_debug.rb
CHANGED
data/test/test_parser_usual.rb
CHANGED
|
@@ -114,6 +114,30 @@ class UsualTest < Minitest::Test
|
|
|
114
114
|
assert_equal(Float, doc.class)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
def test_multi_parse
|
|
118
|
+
p = Oj::Parser.new(:usual)
|
|
119
|
+
out = []
|
|
120
|
+
p.parse('{"a":1}{"b":{"x":2}} {"c":3}') { |j| out.push(j) }
|
|
121
|
+
assert_equal([{'a'=>1}, {'b'=>{'x'=>2}},{'c'=>3}], out)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_multi_load
|
|
125
|
+
p = Oj::Parser.new(:usual)
|
|
126
|
+
out = []
|
|
127
|
+
r, w = IO.pipe
|
|
128
|
+
thread = Thread.new do
|
|
129
|
+
['{"a":1}', '{"b":{"x"', ':2}}{"c":', '3}'].each { |seg|
|
|
130
|
+
w.write(seg)
|
|
131
|
+
sleep(0.1)
|
|
132
|
+
}
|
|
133
|
+
w.close
|
|
134
|
+
end
|
|
135
|
+
p.load(r) { |j| out.push(j) }
|
|
136
|
+
r.close
|
|
137
|
+
thread.join
|
|
138
|
+
assert_equal([{'a'=>1}, {'b'=>{'x'=>2}},{'c'=>3}], out)
|
|
139
|
+
end
|
|
140
|
+
|
|
117
141
|
def test_omit_null
|
|
118
142
|
p = Oj::Parser.new(:usual)
|
|
119
143
|
p.omit_null = true
|
|
@@ -147,6 +171,16 @@ class UsualTest < Minitest::Test
|
|
|
147
171
|
assert_equal(MyHash, doc.class)
|
|
148
172
|
end
|
|
149
173
|
|
|
174
|
+
def test_empty
|
|
175
|
+
p = Oj::Parser.new(:usual)
|
|
176
|
+
p.raise_on_empty = false
|
|
177
|
+
doc = p.parse(' ')
|
|
178
|
+
assert_nil(doc)
|
|
179
|
+
|
|
180
|
+
p.raise_on_empty = true
|
|
181
|
+
assert_raises(Oj::ParseError) { p.parse(' ') }
|
|
182
|
+
end
|
|
183
|
+
|
|
150
184
|
class MyClass
|
|
151
185
|
attr_accessor :a
|
|
152
186
|
attr_accessor :b
|