oj 2.8.1 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/README.md +4 -3
- data/ext/oj/dump.c +123 -53
- data/ext/oj/object.c +32 -9
- data/ext/oj/oj.c +22 -0
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parse.c +20 -1
- data/ext/oj/parse.h +1 -0
- data/ext/oj/saj.c +5 -5
- data/ext/oj/scp.c +9 -2
- data/lib/oj/version.rb +1 -1
- data/test/test_object.rb +60 -1
- data/test/test_scp.rb +16 -0
- data/test/test_strict.rb +7 -0
- metadata +51 -49
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9dd0aba891fc55691f3a56dfeebb3a9b429a3e9
|
4
|
+
data.tar.gz: df3fc2acd752b893079e08bad70b7bcafee0df15
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e0c17e03b86f6e6b495ee3c75cfac64a400e04cd6c3fac52e444d2f48af44d4f69ea327a1ef4db39692fec5e16ca2270c80b4bcc8d66ba80da22a27d492d443
|
7
|
+
data.tar.gz: 91a9dc1eafd5182a4c1d6d930a9b673b88a3151cb9dff09580943f04eff7a323bb4c4fe25de1df34e427dab08afdc9dd587fd7a6b61d43d2ffe29e6f648ad949
|
data/README.md
CHANGED
@@ -26,11 +26,12 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
|
|
26
26
|
|
27
27
|
[![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
|
28
28
|
|
29
|
-
### Current Release 2.
|
29
|
+
### Current Release 2.9.0
|
30
30
|
|
31
|
-
- Added
|
31
|
+
- Added support for detection and handling of String, Array, and Hash subclasses.
|
32
32
|
|
33
|
-
-
|
33
|
+
- Oj.load() can now take a block which will be yielded to on every object
|
34
|
+
parsed when used with a file or string with multiple JSON entries.
|
34
35
|
|
35
36
|
[Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
|
36
37
|
|
data/ext/oj/dump.c
CHANGED
@@ -70,16 +70,16 @@ static void dump_raw(const char *str, size_t cnt, Out out);
|
|
70
70
|
static void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
|
71
71
|
static void dump_hex(uint8_t c, Out out);
|
72
72
|
static void dump_str_comp(VALUE obj, Out out);
|
73
|
-
static void dump_str_obj(VALUE obj, int depth, Out out);
|
73
|
+
static void dump_str_obj(VALUE obj, VALUE clas, int depth, Out out);
|
74
74
|
static void dump_sym_comp(VALUE obj, Out out);
|
75
75
|
static void dump_sym_obj(VALUE obj, Out out);
|
76
76
|
static void dump_class_comp(VALUE obj, Out out);
|
77
77
|
static void dump_class_obj(VALUE obj, Out out);
|
78
|
-
static void dump_array(VALUE obj, int depth, Out out);
|
78
|
+
static void dump_array(VALUE obj, VALUE clas, int depth, Out out);
|
79
79
|
static int hash_cb_strict(VALUE key, VALUE value, Out out);
|
80
80
|
static int hash_cb_compat(VALUE key, VALUE value, Out out);
|
81
81
|
static int hash_cb_object(VALUE key, VALUE value, Out out);
|
82
|
-
static void dump_hash(VALUE obj, int depth, int mode, Out out);
|
82
|
+
static void dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out);
|
83
83
|
static void dump_time(VALUE obj, Out out);
|
84
84
|
static void dump_ruby_time(VALUE obj, Out out);
|
85
85
|
static void dump_xml_time(VALUE obj, Out out);
|
@@ -545,12 +545,9 @@ dump_str_comp(VALUE obj, Out out) {
|
|
545
545
|
}
|
546
546
|
|
547
547
|
static void
|
548
|
-
dump_str_obj(VALUE obj, int depth, Out out) {
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
if (rb_cString != clas && 0 != (odd = oj_get_odd(clas))) {
|
553
|
-
dump_odd(obj, odd, clas, depth + 1, out);
|
548
|
+
dump_str_obj(VALUE obj, VALUE clas, int depth, Out out) {
|
549
|
+
if (Qundef != clas && rb_cString != clas) {
|
550
|
+
dump_obj_attrs(obj, clas, 0, depth, out);
|
554
551
|
} else {
|
555
552
|
const char *s = rb_string_value_ptr((VALUE*)&obj);
|
556
553
|
size_t len = RSTRING_LEN(obj);
|
@@ -602,7 +599,7 @@ dump_class_obj(VALUE obj, Out out) {
|
|
602
599
|
}
|
603
600
|
|
604
601
|
static void
|
605
|
-
dump_array(VALUE a, int depth, Out out) {
|
602
|
+
dump_array(VALUE a, VALUE clas, int depth, Out out) {
|
606
603
|
size_t size;
|
607
604
|
int i, cnt;
|
608
605
|
int d2 = depth + 1;
|
@@ -611,6 +608,10 @@ dump_array(VALUE a, int depth, Out out) {
|
|
611
608
|
if (id < 0) {
|
612
609
|
return;
|
613
610
|
}
|
611
|
+
if (Qundef != clas && rb_cArray != clas) {
|
612
|
+
dump_obj_attrs(a, clas, 0, depth, out);
|
613
|
+
return;
|
614
|
+
}
|
614
615
|
cnt = (int)RARRAY_LEN(a);
|
615
616
|
*out->cur++ = '[';
|
616
617
|
if (0 < id) {
|
@@ -819,7 +820,7 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
|
|
819
820
|
}
|
820
821
|
fill_indent(out, depth);
|
821
822
|
if (rb_type(key) == T_STRING) {
|
822
|
-
dump_str_obj(key, depth, out);
|
823
|
+
dump_str_obj(key, Qundef, depth, out);
|
823
824
|
*out->cur++ = ':';
|
824
825
|
dump_val(value, depth, out);
|
825
826
|
} else if (rb_type(key) == T_SYMBOL) {
|
@@ -873,10 +874,16 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
|
|
873
874
|
}
|
874
875
|
|
875
876
|
static void
|
876
|
-
dump_hash(VALUE obj, int depth, int mode, Out out) {
|
877
|
-
int cnt
|
878
|
-
size_t size
|
877
|
+
dump_hash(VALUE obj, VALUE clas, int depth, int mode, Out out) {
|
878
|
+
int cnt;
|
879
|
+
size_t size;
|
879
880
|
|
881
|
+
if (Qundef != clas && rb_cHash != clas && ObjectMode == mode) {
|
882
|
+
dump_obj_attrs(obj, clas, 0, depth, out);
|
883
|
+
return;
|
884
|
+
}
|
885
|
+
cnt = (int)RHASH_SIZE(obj);
|
886
|
+
size = depth * out->indent + 2;
|
880
887
|
if (out->end - out->cur <= 2) {
|
881
888
|
grow(out, 2);
|
882
889
|
}
|
@@ -1144,7 +1151,7 @@ dump_data_comp(VALUE obj, int depth, Out out) {
|
|
1144
1151
|
if (T_HASH != rb_type(h)) {
|
1145
1152
|
rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1146
1153
|
}
|
1147
|
-
dump_hash(h, depth, out->opts->mode, out);
|
1154
|
+
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1148
1155
|
} else if (rb_respond_to(obj, oj_as_json_id) && obj != (o2 = rb_funcall(obj, oj_as_json_id, 0))) {
|
1149
1156
|
dump_val(o2, depth, out);
|
1150
1157
|
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
@@ -1206,22 +1213,16 @@ dump_data_obj(VALUE obj, int depth, Out out) {
|
|
1206
1213
|
*out->cur++ = '}';
|
1207
1214
|
*out->cur = '\0';
|
1208
1215
|
} else {
|
1209
|
-
|
1210
|
-
|
1211
|
-
if (0 == odd) {
|
1212
|
-
if (oj_bigdecimal_class == clas) {
|
1213
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1216
|
+
if (oj_bigdecimal_class == clas) {
|
1217
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1214
1218
|
|
1215
|
-
|
1216
|
-
|
1217
|
-
} else {
|
1218
|
-
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1219
|
-
}
|
1219
|
+
if (Yes == out->opts->bigdec_as_num) {
|
1220
|
+
dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
|
1220
1221
|
} else {
|
1221
|
-
|
1222
|
+
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1222
1223
|
}
|
1223
1224
|
} else {
|
1224
|
-
|
1225
|
+
dump_nil(out);
|
1225
1226
|
}
|
1226
1227
|
}
|
1227
1228
|
}
|
@@ -1234,7 +1235,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
|
|
1234
1235
|
if (T_HASH != rb_type(h)) {
|
1235
1236
|
rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1236
1237
|
}
|
1237
|
-
dump_hash(h, depth, out->opts->mode, out);
|
1238
|
+
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1238
1239
|
} else if (rb_respond_to(obj, oj_as_json_id)) {
|
1239
1240
|
dump_val(rb_funcall(obj, oj_as_json_id, 0), depth, out);
|
1240
1241
|
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
@@ -1268,13 +1269,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
|
|
1268
1269
|
|
1269
1270
|
dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1270
1271
|
} else {
|
1271
|
-
|
1272
|
-
|
1273
|
-
if (0 == odd) {
|
1274
|
-
dump_obj_attrs(obj, 0, 0, depth, out);
|
1275
|
-
} else {
|
1276
|
-
dump_odd(obj, odd, 0, depth + 1, out);
|
1277
|
-
}
|
1272
|
+
dump_obj_attrs(obj, Qundef, 0, depth, out);
|
1278
1273
|
}
|
1279
1274
|
}
|
1280
1275
|
*out->cur = '\0';
|
@@ -1286,22 +1281,39 @@ dump_obj_obj(VALUE obj, int depth, Out out) {
|
|
1286
1281
|
|
1287
1282
|
if (0 <= id) {
|
1288
1283
|
VALUE clas = rb_obj_class(obj);
|
1289
|
-
Odd odd = oj_get_odd(clas);
|
1290
1284
|
|
1291
|
-
if (
|
1292
|
-
|
1293
|
-
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1285
|
+
if (oj_bigdecimal_class == clas) {
|
1286
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1294
1287
|
|
1295
|
-
|
1296
|
-
} else {
|
1297
|
-
dump_obj_attrs(obj, clas, id, depth, out);
|
1298
|
-
}
|
1288
|
+
dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
|
1299
1289
|
} else {
|
1300
|
-
|
1290
|
+
dump_obj_attrs(obj, clas, id, depth, out);
|
1301
1291
|
}
|
1302
1292
|
}
|
1303
1293
|
}
|
1304
1294
|
|
1295
|
+
#ifdef RUBINIUS_RUBY
|
1296
|
+
static int
|
1297
|
+
isRbxHashAttr(const char *attr) {
|
1298
|
+
const char *hashAttrs[] = {
|
1299
|
+
"@capacity",
|
1300
|
+
"@max_entries",
|
1301
|
+
"@state",
|
1302
|
+
"@mask",
|
1303
|
+
"@size",
|
1304
|
+
"@entries",
|
1305
|
+
0 };
|
1306
|
+
const char **ap;
|
1307
|
+
|
1308
|
+
for (ap = hashAttrs; 0 != *ap; ap++) {
|
1309
|
+
if (0 == strcmp(attr, *ap)) {
|
1310
|
+
return 1;
|
1311
|
+
}
|
1312
|
+
}
|
1313
|
+
return 0;
|
1314
|
+
}
|
1315
|
+
#endif
|
1316
|
+
|
1305
1317
|
#if HAS_IVAR_HELPERS
|
1306
1318
|
static int
|
1307
1319
|
dump_attr_cb(ID key, VALUE value, Out out) {
|
@@ -1342,12 +1354,13 @@ static void
|
|
1342
1354
|
dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
1343
1355
|
size_t size = 0;
|
1344
1356
|
int d2 = depth + 1;
|
1357
|
+
int type = rb_type(obj);
|
1345
1358
|
|
1346
1359
|
if (out->end - out->cur <= 2) {
|
1347
1360
|
grow(out, 2);
|
1348
1361
|
}
|
1349
1362
|
*out->cur++ = '{';
|
1350
|
-
if (
|
1363
|
+
if (Qundef != clas) {
|
1351
1364
|
const char *class_name = rb_class2name(clas);
|
1352
1365
|
int clen = (int)strlen(class_name);
|
1353
1366
|
|
@@ -1377,6 +1390,58 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
|
1377
1390
|
*out->cur++ = ':';
|
1378
1391
|
dump_ulong(id, out);
|
1379
1392
|
}
|
1393
|
+
switch (type) {
|
1394
|
+
case T_STRING:
|
1395
|
+
size = d2 * out->indent + 14;
|
1396
|
+
if (out->end - out->cur <= (long)size) {
|
1397
|
+
grow(out, size);
|
1398
|
+
}
|
1399
|
+
*out->cur++ = ',';
|
1400
|
+
fill_indent(out, d2);
|
1401
|
+
*out->cur++ = '"';
|
1402
|
+
*out->cur++ = 's';
|
1403
|
+
*out->cur++ = 'e';
|
1404
|
+
*out->cur++ = 'l';
|
1405
|
+
*out->cur++ = 'f';
|
1406
|
+
*out->cur++ = '"';
|
1407
|
+
*out->cur++ = ':';
|
1408
|
+
dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
|
1409
|
+
break;
|
1410
|
+
case T_ARRAY:
|
1411
|
+
size = d2 * out->indent + 14;
|
1412
|
+
if (out->end - out->cur <= (long)size) {
|
1413
|
+
grow(out, size);
|
1414
|
+
}
|
1415
|
+
*out->cur++ = ',';
|
1416
|
+
fill_indent(out, d2);
|
1417
|
+
*out->cur++ = '"';
|
1418
|
+
*out->cur++ = 's';
|
1419
|
+
*out->cur++ = 'e';
|
1420
|
+
*out->cur++ = 'l';
|
1421
|
+
*out->cur++ = 'f';
|
1422
|
+
*out->cur++ = '"';
|
1423
|
+
*out->cur++ = ':';
|
1424
|
+
dump_array(obj, Qundef, depth + 1, out);
|
1425
|
+
break;
|
1426
|
+
case T_HASH:
|
1427
|
+
size = d2 * out->indent + 14;
|
1428
|
+
if (out->end - out->cur <= (long)size) {
|
1429
|
+
grow(out, size);
|
1430
|
+
}
|
1431
|
+
*out->cur++ = ',';
|
1432
|
+
fill_indent(out, d2);
|
1433
|
+
*out->cur++ = '"';
|
1434
|
+
*out->cur++ = 's';
|
1435
|
+
*out->cur++ = 'e';
|
1436
|
+
*out->cur++ = 'l';
|
1437
|
+
*out->cur++ = 'f';
|
1438
|
+
*out->cur++ = '"';
|
1439
|
+
*out->cur++ = ':';
|
1440
|
+
dump_hash(obj, Qundef, depth + 1, out->opts->mode, out);
|
1441
|
+
break;
|
1442
|
+
default:
|
1443
|
+
break;
|
1444
|
+
}
|
1380
1445
|
{
|
1381
1446
|
int cnt;
|
1382
1447
|
#if HAS_IVAR_HELPERS
|
@@ -1390,7 +1455,7 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
|
1390
1455
|
|
1391
1456
|
cnt = (int)RARRAY_LEN(vars);
|
1392
1457
|
#endif
|
1393
|
-
if (
|
1458
|
+
if (Qundef != clas && 0 < cnt) {
|
1394
1459
|
*out->cur++ = ',';
|
1395
1460
|
}
|
1396
1461
|
out->depth = depth + 1;
|
@@ -1402,12 +1467,17 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
|
1402
1467
|
#else
|
1403
1468
|
size = d2 * out->indent + 1;
|
1404
1469
|
for (i = cnt; 0 < i; i--, np++) {
|
1470
|
+
vid = rb_to_id(*np);
|
1471
|
+
attr = rb_id2name(vid);
|
1472
|
+
#ifdef RUBINIUS_RUBY
|
1473
|
+
if (T_HASH == type && isRbxHashAttr(attr)) {
|
1474
|
+
continue;
|
1475
|
+
}
|
1476
|
+
#endif
|
1405
1477
|
if (out->end - out->cur <= (long)size) {
|
1406
1478
|
grow(out, size);
|
1407
1479
|
}
|
1408
|
-
vid = rb_to_id(*np);
|
1409
1480
|
fill_indent(out, d2);
|
1410
|
-
attr = rb_id2name(vid);
|
1411
1481
|
if ('@' == *attr) {
|
1412
1482
|
attr++;
|
1413
1483
|
dump_cstr(attr, strlen(attr), 0, 0, out);
|
@@ -1478,7 +1548,7 @@ dump_struct_comp(VALUE obj, int depth, Out out) {
|
|
1478
1548
|
if (T_HASH != rb_type(h)) {
|
1479
1549
|
rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
1480
1550
|
}
|
1481
|
-
dump_hash(h, depth, out->opts->mode, out);
|
1551
|
+
dump_hash(h, Qundef, depth, out->opts->mode, out);
|
1482
1552
|
} else if (rb_respond_to(obj, oj_to_json_id)) {
|
1483
1553
|
volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
|
1484
1554
|
const char *s;
|
@@ -1571,7 +1641,7 @@ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
|
|
1571
1641
|
grow(out, 2);
|
1572
1642
|
}
|
1573
1643
|
*out->cur++ = '{';
|
1574
|
-
if (
|
1644
|
+
if (Qundef != clas) {
|
1575
1645
|
const char *class_name = rb_class2name(clas);
|
1576
1646
|
int clen = (int)strlen(class_name);
|
1577
1647
|
|
@@ -1674,11 +1744,11 @@ dump_val(VALUE obj, int depth, Out out) {
|
|
1674
1744
|
case NullMode:
|
1675
1745
|
case CompatMode: dump_str_comp(obj, out); break;
|
1676
1746
|
case ObjectMode:
|
1677
|
-
default: dump_str_obj(obj, depth, out); break;
|
1747
|
+
default: dump_str_obj(obj, clas, depth, out); break;
|
1678
1748
|
}
|
1679
1749
|
break;
|
1680
|
-
case T_ARRAY: dump_array(obj, depth, out); break;
|
1681
|
-
case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
|
1750
|
+
case T_ARRAY: dump_array(obj, clas, depth, out); break;
|
1751
|
+
case T_HASH: dump_hash(obj, clas, depth, out->opts->mode, out); break;
|
1682
1752
|
#if (defined T_RATIONAL && defined RRATIONAL)
|
1683
1753
|
case T_RATIONAL:
|
1684
1754
|
#endif
|
data/ext/oj/object.c
CHANGED
@@ -330,6 +330,13 @@ hash_set_cstr(ParseInfo pi, const char *key, size_t klen, const char *str, size_
|
|
330
330
|
case T_HASH:
|
331
331
|
rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), str_to_value(pi, str, len, orig));
|
332
332
|
break;
|
333
|
+
case T_STRING:
|
334
|
+
if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
|
335
|
+
rb_funcall(parent->val, oj_replace_id, 1, str_to_value(pi, str, len, orig));
|
336
|
+
} else {
|
337
|
+
set_obj_ivar(parent, key, klen, str_to_value(pi, str, len, orig));
|
338
|
+
}
|
339
|
+
break;
|
333
340
|
case T_OBJECT:
|
334
341
|
set_obj_ivar(parent, key, klen, str_to_value(pi, str, len, orig));
|
335
342
|
break;
|
@@ -413,19 +420,36 @@ hash_set_value(ParseInfo pi, const char *key, size_t klen, VALUE value) {
|
|
413
420
|
}
|
414
421
|
break;
|
415
422
|
case T_HASH:
|
416
|
-
|
417
|
-
|
418
|
-
|
423
|
+
|
424
|
+
if (rb_cHash != rb_obj_class(parent->val)) {
|
425
|
+
if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
|
426
|
+
rb_funcall(parent->val, oj_replace_id, 1, value);
|
427
|
+
} else {
|
428
|
+
set_obj_ivar(parent, key, klen, value);
|
429
|
+
}
|
430
|
+
} else {
|
431
|
+
if (3 <= klen && '#' == key[1] && T_ARRAY == rb_type(value)) {
|
432
|
+
long len = RARRAY_LEN(value);
|
433
|
+
VALUE *a = RARRAY_PTR(value);
|
419
434
|
|
420
|
-
|
421
|
-
|
422
|
-
|
435
|
+
if (2 != len) {
|
436
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
|
437
|
+
return;
|
438
|
+
}
|
439
|
+
rb_hash_aset(parent->val, *a, a[1]);
|
440
|
+
} else {
|
441
|
+
rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), value);
|
423
442
|
}
|
424
|
-
|
443
|
+
}
|
444
|
+
break;
|
445
|
+
case T_ARRAY:
|
446
|
+
if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
|
447
|
+
rb_funcall(parent->val, oj_replace_id, 1, value);
|
425
448
|
} else {
|
426
|
-
|
449
|
+
set_obj_ivar(parent, key, klen, value);
|
427
450
|
}
|
428
451
|
break;
|
452
|
+
case T_STRING: // for subclassed strings
|
429
453
|
case T_OBJECT:
|
430
454
|
set_obj_ivar(parent, key, klen, value);
|
431
455
|
break;
|
@@ -450,7 +474,6 @@ hash_set_value(ParseInfo pi, const char *key, size_t klen, VALUE value) {
|
|
450
474
|
}
|
451
475
|
}
|
452
476
|
|
453
|
-
|
454
477
|
static VALUE
|
455
478
|
start_hash(ParseInfo pi) {
|
456
479
|
return Qnil;
|
data/ext/oj/oj.c
CHANGED
@@ -67,6 +67,7 @@ ID oj_json_create_id;
|
|
67
67
|
ID oj_length_id;
|
68
68
|
ID oj_new_id;
|
69
69
|
ID oj_read_id;
|
70
|
+
ID oj_replace_id;
|
70
71
|
ID oj_string_id;
|
71
72
|
ID oj_to_hash_id;
|
72
73
|
ID oj_to_json_id;
|
@@ -536,6 +537,10 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
536
537
|
* Object type. These two are not the same since the JSON Object type can have
|
537
538
|
* repeating entries with the same key and Ruby Hash can not.
|
538
539
|
*
|
540
|
+
* When used with a document that has multiple JSON elements the block, if
|
541
|
+
* any, will be yielded to. If no block then the last element read will be
|
542
|
+
* returned.
|
543
|
+
*
|
539
544
|
* Raises an exception if the JSON is malformed or the classes specified are not
|
540
545
|
* valid. If the input is not a valid JSON document (an empty string is not a
|
541
546
|
* valid JSON document) an exception is raised.
|
@@ -553,6 +558,10 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
553
558
|
* on the :create_id value. It is not compatible in every way to every other
|
554
559
|
* parser though as each parser has it's own variations.
|
555
560
|
*
|
561
|
+
* When used with a document that has multiple JSON elements the block, if
|
562
|
+
* any, will be yielded to. If no block then the last element read will be
|
563
|
+
* returned.
|
564
|
+
*
|
556
565
|
* Raises an exception if the JSON is malformed or the classes specified are not
|
557
566
|
* valid. If the input is not a valid JSON document (an empty string is not a
|
558
567
|
* valid JSON document) an exception is raised.
|
@@ -570,6 +579,10 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
570
579
|
* or dumped Object. The :auto_define and :circular options have meaning with
|
571
580
|
* this parsing mode.
|
572
581
|
*
|
582
|
+
* When used with a document that has multiple JSON elements the block, if
|
583
|
+
* any, will be yielded to. If no block then the last element read will be
|
584
|
+
* returned.
|
585
|
+
*
|
573
586
|
* Raises an exception if the JSON is malformed or the classes specified are not
|
574
587
|
* valid. If the input is not a valid JSON document (an empty string is not a
|
575
588
|
* valid JSON document) an exception is raised.
|
@@ -602,6 +615,10 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
602
615
|
* specified are not valid. If the string input is not a valid JSON document (an
|
603
616
|
* empty string is not a valid JSON document) an exception is raised.
|
604
617
|
*
|
618
|
+
* When used with a document that has multiple JSON elements the block, if
|
619
|
+
* any, will be yielded to. If no block then the last element read will be
|
620
|
+
* returned.
|
621
|
+
*
|
605
622
|
* @param [String|IO] json JSON String or an Object that responds to read()
|
606
623
|
* @param [Hash] options load options (same as default_options)
|
607
624
|
*/
|
@@ -652,6 +669,10 @@ load(int argc, VALUE *argv, VALUE self) {
|
|
652
669
|
* specified are not valid. If the string input is not a valid JSON document (an
|
653
670
|
* empty string is not a valid JSON document) an exception is raised.
|
654
671
|
*
|
672
|
+
* When used with a document that has multiple JSON elements the block, if
|
673
|
+
* any, will be yielded to. If no block then the last element read will be
|
674
|
+
* returned.
|
675
|
+
*
|
655
676
|
* If the input file is not a valid JSON document (an empty file is not a valid
|
656
677
|
* JSON document) an exception is raised.
|
657
678
|
*
|
@@ -1857,6 +1878,7 @@ void Init_oj() {
|
|
1857
1878
|
oj_length_id = rb_intern("length");
|
1858
1879
|
oj_new_id = rb_intern("new");
|
1859
1880
|
oj_read_id = rb_intern("read");
|
1881
|
+
oj_replace_id = rb_intern("replace");
|
1860
1882
|
oj_string_id = rb_intern("string");
|
1861
1883
|
oj_to_hash_id = rb_intern("to_hash");
|
1862
1884
|
oj_to_json_id = rb_intern("to_json");
|
data/ext/oj/oj.h
CHANGED
data/ext/oj/parse.c
CHANGED
@@ -640,6 +640,20 @@ oj_parse2(ParseInfo pi) {
|
|
640
640
|
if (err_has(&pi->err)) {
|
641
641
|
return;
|
642
642
|
}
|
643
|
+
if (Qundef != pi->proc && stack_empty(&pi->stack)) {
|
644
|
+
if (Qnil == pi->proc) {
|
645
|
+
rb_yield(stack_head_val(&pi->stack));
|
646
|
+
} else {
|
647
|
+
#if HAS_PROC_WITH_BLOCK
|
648
|
+
VALUE args[1];
|
649
|
+
|
650
|
+
*args = stack_head_val(&pi->stack);
|
651
|
+
rb_proc_call_with_block(pi->proc, 1, args, Qnil);
|
652
|
+
#else
|
653
|
+
rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
|
654
|
+
#endif
|
655
|
+
}
|
656
|
+
}
|
643
657
|
}
|
644
658
|
}
|
645
659
|
|
@@ -734,6 +748,11 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len) {
|
|
734
748
|
if (2 == argc) {
|
735
749
|
oj_parse_options(argv[1], &pi->options);
|
736
750
|
}
|
751
|
+
if (rb_block_given_p()) {
|
752
|
+
pi->proc = Qnil;
|
753
|
+
} else {
|
754
|
+
pi->proc = Qundef;
|
755
|
+
}
|
737
756
|
pi->cbc = (void*)0;
|
738
757
|
if (0 != json) {
|
739
758
|
pi->json = json;
|
@@ -803,7 +822,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len) {
|
|
803
822
|
if (No == pi->options.allow_gc) {
|
804
823
|
rb_gc_enable();
|
805
824
|
}
|
806
|
-
{
|
825
|
+
if (!err_has(&pi->err)) {
|
807
826
|
// If the stack is not empty then the JSON terminated early.
|
808
827
|
Val v;
|
809
828
|
|
data/ext/oj/parse.h
CHANGED
@@ -63,6 +63,7 @@ typedef struct _ParseInfo {
|
|
63
63
|
struct _ValStack stack;
|
64
64
|
CircArray circ_array;
|
65
65
|
int expect_value;
|
66
|
+
VALUE proc;
|
66
67
|
VALUE (*start_hash)(struct _ParseInfo *pi);
|
67
68
|
void (*end_hash)(struct _ParseInfo *pi);
|
68
69
|
void (*hash_set_cstr)(struct _ParseInfo *pi, const char *key, size_t klen, const char *str, size_t len, const char *orig);
|
data/ext/oj/saj.c
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
/*
|
1
|
+
/* saj.c
|
2
2
|
* Copyright (c) 2012, Peter Ohler
|
3
3
|
* All rights reserved.
|
4
4
|
*
|
@@ -644,7 +644,7 @@ respond_to(VALUE obj, ID method) {
|
|
644
644
|
}
|
645
645
|
|
646
646
|
static void
|
647
|
-
|
647
|
+
saj_parse(VALUE handler, char *json) {
|
648
648
|
volatile VALUE obj = Qnil;
|
649
649
|
struct _ParseInfo pi;
|
650
650
|
|
@@ -692,11 +692,11 @@ sajkey_parse(VALUE handler, char *json) {
|
|
692
692
|
}
|
693
693
|
}
|
694
694
|
|
695
|
-
/* call-seq:
|
695
|
+
/* call-seq: saj_parse(handler, io)
|
696
696
|
*
|
697
697
|
* Parses an IO stream or file containing an JSON document. Raises an exception
|
698
698
|
* if the JSON is malformed.
|
699
|
-
* @param [Oj::
|
699
|
+
* @param [Oj::Saj] handler Saj (responds to Oj::Saj methods) like handler
|
700
700
|
* @param [IO|String] io IO Object to read from
|
701
701
|
*/
|
702
702
|
VALUE
|
@@ -749,7 +749,7 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
749
749
|
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
|
750
750
|
}
|
751
751
|
}
|
752
|
-
|
752
|
+
saj_parse(*argv, json);
|
753
753
|
xfree(json);
|
754
754
|
|
755
755
|
return Qnil;
|
data/ext/oj/scp.c
CHANGED
@@ -211,6 +211,11 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
|
|
211
211
|
if (3 == argc) {
|
212
212
|
oj_parse_options(argv[2], &pi.options);
|
213
213
|
}
|
214
|
+
if (rb_block_given_p()) {
|
215
|
+
pi.proc = Qnil;
|
216
|
+
} else {
|
217
|
+
pi.proc = Qundef;
|
218
|
+
}
|
214
219
|
pi.cbc = (void*)handler;
|
215
220
|
|
216
221
|
pi.start_hash = respond_to(handler, oj_hash_start_id) ? start_hash : noop_start;
|
@@ -255,6 +260,7 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
|
|
255
260
|
} else {
|
256
261
|
VALUE clas = rb_obj_class(input);
|
257
262
|
volatile VALUE s;
|
263
|
+
int fd;
|
258
264
|
|
259
265
|
if (oj_stringio_class == clas) {
|
260
266
|
s = rb_funcall2(input, oj_string_id, 0, 0);
|
@@ -262,8 +268,9 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
|
|
262
268
|
#ifndef JRUBY_RUBY
|
263
269
|
#if !IS_WINDOWS
|
264
270
|
// JRuby gets confused with what is the real fileno.
|
265
|
-
} else if (rb_respond_to(input, oj_fileno_id) &&
|
266
|
-
|
271
|
+
} else if (rb_respond_to(input, oj_fileno_id) &&
|
272
|
+
Qnil != (s = rb_funcall(input, oj_fileno_id, 0)) &&
|
273
|
+
0 != (fd = FIX2INT(s))) {
|
267
274
|
ssize_t cnt;
|
268
275
|
size_t len = lseek(fd, 0, SEEK_END);
|
269
276
|
|
data/lib/oj/version.rb
CHANGED
data/test/test_object.rb
CHANGED
@@ -102,6 +102,48 @@ class Strung < String
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
class AutoStrung < String
|
106
|
+
attr_accessor :safe
|
107
|
+
|
108
|
+
def initialize(str, safe)
|
109
|
+
super(str)
|
110
|
+
@safe = safe
|
111
|
+
end
|
112
|
+
|
113
|
+
def eql?(o)
|
114
|
+
self.class == o.class && super(o) && @safe == o.safe
|
115
|
+
end
|
116
|
+
alias == eql?
|
117
|
+
end
|
118
|
+
|
119
|
+
class AutoArray < Array
|
120
|
+
attr_accessor :safe
|
121
|
+
|
122
|
+
def initialize(a, safe)
|
123
|
+
super(a)
|
124
|
+
@safe = safe
|
125
|
+
end
|
126
|
+
|
127
|
+
def eql?(o)
|
128
|
+
self.class == o.class && super(o) && @safe == o.safe
|
129
|
+
end
|
130
|
+
alias == eql?
|
131
|
+
end
|
132
|
+
|
133
|
+
class AutoHash < Hash
|
134
|
+
attr_accessor :safe
|
135
|
+
|
136
|
+
def initialize(h, safe)
|
137
|
+
super(h)
|
138
|
+
@safe = safe
|
139
|
+
end
|
140
|
+
|
141
|
+
def eql?(o)
|
142
|
+
self.class == o.class && super(o) && @safe == o.safe
|
143
|
+
end
|
144
|
+
alias == eql?
|
145
|
+
end
|
146
|
+
|
105
147
|
def hash_eql(h1, h2)
|
106
148
|
return false if h1.size != h2.size
|
107
149
|
h1.keys.each do |k|
|
@@ -418,7 +460,24 @@ class ObjectJuice < ::Test::Unit::TestCase
|
|
418
460
|
def test_odd_string
|
419
461
|
Oj.register_odd(Strung, Strung, :create, :to_s, 'safe?')
|
420
462
|
s = Strung.new("Pete", true)
|
421
|
-
dump_and_load(
|
463
|
+
dump_and_load(s, false)
|
464
|
+
end
|
465
|
+
|
466
|
+
def test_auto_string
|
467
|
+
s = AutoStrung.new("Pete", true)
|
468
|
+
dump_and_load(s, false)
|
469
|
+
end
|
470
|
+
|
471
|
+
def test_auto_array
|
472
|
+
a = AutoArray.new([1, 'abc', nil], true)
|
473
|
+
dump_and_load(a, false)
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_auto_hash
|
477
|
+
h = AutoHash.new(nil, true)
|
478
|
+
h['a'] = 1
|
479
|
+
h['b'] = 2
|
480
|
+
dump_and_load(h, false)
|
422
481
|
end
|
423
482
|
|
424
483
|
def dump_and_load(obj, trace=false)
|
data/test/test_scp.rb
CHANGED
@@ -206,6 +206,22 @@ class ScpTest < ::Test::Unit::TestCase
|
|
206
206
|
[:add_value, {}]], handler.calls)
|
207
207
|
end
|
208
208
|
|
209
|
+
def test_double
|
210
|
+
handler = AllHandler.new()
|
211
|
+
json = %{{"one":true,"two":false}{"three":true,"four":false}}
|
212
|
+
Oj.sc_parse(handler, json)
|
213
|
+
assert_equal([[:hash_start],
|
214
|
+
[:hash_set, 'one', true],
|
215
|
+
[:hash_set, 'two', false],
|
216
|
+
[:hash_end],
|
217
|
+
[:add_value, {}],
|
218
|
+
[:hash_start],
|
219
|
+
[:hash_set, 'three', true],
|
220
|
+
[:hash_set, 'four', false],
|
221
|
+
[:hash_end],
|
222
|
+
[:add_value, {}]], handler.calls)
|
223
|
+
end
|
224
|
+
|
209
225
|
def test_none
|
210
226
|
handler = NoHandler.new()
|
211
227
|
Oj.sc_parse(handler, $json)
|
data/test/test_strict.rb
CHANGED
@@ -247,6 +247,13 @@ class StrictJuice < ::Test::Unit::TestCase
|
|
247
247
|
assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
|
248
248
|
end
|
249
249
|
|
250
|
+
def test_double
|
251
|
+
json = %{{ "x": 1}{ "y": 2}}
|
252
|
+
results = []
|
253
|
+
Oj.load(json, :mode => :strict) { |x| results << x }
|
254
|
+
|
255
|
+
assert_equal([{ 'x' => 1 }, { 'y' => 2 }], results)
|
256
|
+
end
|
250
257
|
|
251
258
|
def dump_and_load(obj, trace=false)
|
252
259
|
json = Oj.dump(obj, :indent => 2)
|
metadata
CHANGED
@@ -1,60 +1,64 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.9.0
|
6
5
|
platform: ruby
|
7
|
-
authors:
|
6
|
+
authors:
|
8
7
|
- Peter Ohler
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
|
11
|
+
|
12
|
+
date: 2014-05-01 00:00:00 Z
|
13
13
|
dependencies: []
|
14
|
-
|
14
|
+
|
15
|
+
description: "The fastest JSON parser and object serializer. "
|
15
16
|
email: peter@ohler.com
|
16
17
|
executables: []
|
17
|
-
|
18
|
+
|
19
|
+
extensions:
|
18
20
|
- ext/oj/extconf.rb
|
19
|
-
extra_rdoc_files:
|
21
|
+
extra_rdoc_files:
|
22
|
+
- README.md
|
23
|
+
files:
|
24
|
+
- LICENSE
|
20
25
|
- README.md
|
21
|
-
files:
|
22
|
-
- lib/oj/bag.rb
|
23
|
-
- lib/oj/error.rb
|
24
|
-
- lib/oj/mimic.rb
|
25
|
-
- lib/oj/saj.rb
|
26
|
-
- lib/oj/schandler.rb
|
27
|
-
- lib/oj/version.rb
|
28
|
-
- lib/oj.rb
|
29
|
-
- ext/oj/extconf.rb
|
30
26
|
- ext/oj/buf.h
|
31
|
-
- ext/oj/cache8.h
|
32
|
-
- ext/oj/circarray.h
|
33
|
-
- ext/oj/encode.h
|
34
|
-
- ext/oj/err.h
|
35
|
-
- ext/oj/hash.h
|
36
|
-
- ext/oj/odd.h
|
37
|
-
- ext/oj/oj.h
|
38
|
-
- ext/oj/parse.h
|
39
|
-
- ext/oj/resolve.h
|
40
|
-
- ext/oj/val_stack.h
|
41
27
|
- ext/oj/cache8.c
|
28
|
+
- ext/oj/cache8.h
|
42
29
|
- ext/oj/circarray.c
|
30
|
+
- ext/oj/circarray.h
|
43
31
|
- ext/oj/compat.c
|
44
32
|
- ext/oj/dump.c
|
33
|
+
- ext/oj/encode.h
|
45
34
|
- ext/oj/err.c
|
35
|
+
- ext/oj/err.h
|
36
|
+
- ext/oj/extconf.rb
|
46
37
|
- ext/oj/fast.c
|
47
38
|
- ext/oj/hash.c
|
39
|
+
- ext/oj/hash.h
|
48
40
|
- ext/oj/hash_test.c
|
49
41
|
- ext/oj/object.c
|
50
42
|
- ext/oj/odd.c
|
43
|
+
- ext/oj/odd.h
|
51
44
|
- ext/oj/oj.c
|
45
|
+
- ext/oj/oj.h
|
52
46
|
- ext/oj/parse.c
|
47
|
+
- ext/oj/parse.h
|
53
48
|
- ext/oj/resolve.c
|
49
|
+
- ext/oj/resolve.h
|
54
50
|
- ext/oj/saj.c
|
55
51
|
- ext/oj/scp.c
|
56
52
|
- ext/oj/strict.c
|
57
53
|
- ext/oj/val_stack.c
|
54
|
+
- ext/oj/val_stack.h
|
55
|
+
- lib/oj.rb
|
56
|
+
- lib/oj/bag.rb
|
57
|
+
- lib/oj/error.rb
|
58
|
+
- lib/oj/mimic.rb
|
59
|
+
- lib/oj/saj.rb
|
60
|
+
- lib/oj/schandler.rb
|
61
|
+
- lib/oj/version.rb
|
58
62
|
- test/debian_test.rb
|
59
63
|
- test/files.rb
|
60
64
|
- test/perf.rb
|
@@ -68,6 +72,7 @@ files:
|
|
68
72
|
- test/perf_scp.rb
|
69
73
|
- test/perf_simple.rb
|
70
74
|
- test/perf_strict.rb
|
75
|
+
- test/sample.rb
|
71
76
|
- test/sample/change.rb
|
72
77
|
- test/sample/dir.rb
|
73
78
|
- test/sample/doc.rb
|
@@ -80,7 +85,6 @@ files:
|
|
80
85
|
- test/sample/rect.rb
|
81
86
|
- test/sample/shape.rb
|
82
87
|
- test/sample/text.rb
|
83
|
-
- test/sample.rb
|
84
88
|
- test/sample_json.rb
|
85
89
|
- test/test_compat.rb
|
86
90
|
- test/test_fast.rb
|
@@ -93,36 +97,34 @@ files:
|
|
93
97
|
- test/test_strict.rb
|
94
98
|
- test/test_writer.rb
|
95
99
|
- test/tests.rb
|
96
|
-
- LICENSE
|
97
|
-
- README.md
|
98
100
|
homepage: http://www.ohler.com/oj
|
99
|
-
licenses:
|
101
|
+
licenses:
|
100
102
|
- MIT
|
101
103
|
- GPL-3.0
|
104
|
+
metadata: {}
|
105
|
+
|
102
106
|
post_install_message:
|
103
|
-
rdoc_options:
|
107
|
+
rdoc_options:
|
104
108
|
- --main
|
105
109
|
- README.md
|
106
|
-
require_paths:
|
110
|
+
require_paths:
|
107
111
|
- lib
|
108
112
|
- ext
|
109
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
- !ruby/object:Gem::Version
|
114
|
-
version:
|
115
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
-
|
117
|
-
|
118
|
-
- - ! '>='
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
version: '0'
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- &id001
|
116
|
+
- ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- *id001
|
121
122
|
requirements: []
|
123
|
+
|
122
124
|
rubyforge_project: oj
|
123
|
-
rubygems_version:
|
125
|
+
rubygems_version: 2.2.2
|
124
126
|
signing_key:
|
125
|
-
specification_version:
|
127
|
+
specification_version: 4
|
126
128
|
summary: A fast JSON parser and serializer.
|
127
129
|
test_files: []
|
128
|
-
|
130
|
+
|