oj 3.15.0 → 3.16.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 +4 -4
- data/CHANGELOG.md +15 -0
- data/ext/oj/code.c +3 -10
- data/ext/oj/compat.c +5 -18
- data/ext/oj/custom.c +5 -13
- data/ext/oj/dump.c +25 -20
- data/ext/oj/dump.h +1 -4
- data/ext/oj/dump_compat.c +16 -16
- data/ext/oj/extconf.rb +4 -2
- data/ext/oj/fast.c +3 -6
- data/ext/oj/mimic_json.c +0 -3
- data/ext/oj/oj.c +33 -9
- data/ext/oj/oj.h +23 -30
- data/ext/oj/parse.c +3 -5
- data/ext/oj/parse.h +16 -14
- data/ext/oj/parser.h +2 -2
- data/ext/oj/rails.c +0 -1
- data/ext/oj/reader.c +1 -3
- data/ext/oj/saj.c +1 -1
- data/ext/oj/string_writer.c +0 -1
- data/lib/oj/active_support_helper.rb +2 -3
- data/lib/oj/json.rb +159 -149
- data/lib/oj/mimic.rb +3 -1
- data/lib/oj/version.rb +1 -1
- data/test/foo.rb +3 -4
- data/test/json_gem/json_common_interface_test.rb +4 -2
- data/test/json_gem/json_generator_test.rb +1 -1
- data/test/perf_dump.rb +3 -3
- data/test/prec.rb +4 -4
- data/test/test_compat.rb +19 -1
- data/test/test_parser_debug.rb +1 -1
- data/test/test_various.rb +6 -0
- 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: affe42de16d26dfe301f38df7c0bc8ceee39ab25e5a6bd14bd7af6671cc3b422
|
4
|
+
data.tar.gz: ee1d7227f864b197aca62cb767f9202798b6d3d13cf0b5f89eae3f3a36450dbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dbf6a0b5fef2b179dba864b595113f73ac27a952307559422bf1feda19fcc008a5e43a142d1bb3eb0cc4125ff18f35730208c7f82b11089ff90690e7b1f17d0
|
7
|
+
data.tar.gz: be09c04fa551288e2708c9151d0c966a09c3768ba15442cc54b8b4dd5850e2228b45608c06c8251c4e2ca32333353713f9c1feff81e4d34a48d1c6bf725014f6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.16.0 - 2023-08-16
|
4
|
+
|
5
|
+
- Added the `float_format` option.
|
6
|
+
|
7
|
+
- Expanded the `max_nesting` option to allow integer values as well as
|
8
|
+
the previous boolean (true or nil).
|
9
|
+
|
10
|
+
- Skip nesting tests with Truffle Ruby in the json gem tests.
|
11
|
+
|
12
|
+
## 3.15.1 - 2023-07-30
|
13
|
+
|
14
|
+
- Add protection against some using `require 'oj/json`, an internal file.
|
15
|
+
|
16
|
+
- Fixed non-json errors when in compat mode.
|
17
|
+
|
3
18
|
## 3.15.0 - 2023-06-02
|
4
19
|
|
5
20
|
- Added `omit_null_byte` option when dumping.
|
data/ext/oj/code.c
CHANGED
@@ -185,24 +185,17 @@ void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out, bool with_class) {
|
|
185
185
|
} else {
|
186
186
|
char buf[32];
|
187
187
|
char *b = buf + sizeof(buf) - 1;
|
188
|
-
|
188
|
+
bool neg = false;
|
189
189
|
long num = attrs->num;
|
190
190
|
size_t cnt = 0;
|
191
191
|
|
192
192
|
if (0 > num) {
|
193
|
-
neg =
|
193
|
+
neg = true;
|
194
194
|
num = -num;
|
195
195
|
}
|
196
196
|
*b-- = '\0';
|
197
197
|
if (0 < num) {
|
198
|
-
|
199
|
-
*b = (num % 10) + '0';
|
200
|
-
}
|
201
|
-
if (neg) {
|
202
|
-
*b = '-';
|
203
|
-
} else {
|
204
|
-
b++;
|
205
|
-
}
|
198
|
+
b = oj_longlong_to_string(num, neg, b);
|
206
199
|
} else {
|
207
200
|
*b = '0';
|
208
201
|
}
|
data/ext/oj/compat.c
CHANGED
@@ -13,32 +13,19 @@
|
|
13
13
|
#include "trace.h"
|
14
14
|
|
15
15
|
static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
16
|
-
const char
|
17
|
-
int
|
18
|
-
Val
|
19
|
-
volatile VALUE rkey = kval->key_val;
|
16
|
+
const char *key = kval->key;
|
17
|
+
int klen = kval->klen;
|
18
|
+
Val parent = stack_peek(&pi->stack);
|
20
19
|
|
21
|
-
if (Qundef ==
|
20
|
+
if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
|
22
21
|
*pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
|
23
22
|
0 == strncmp(pi->options.create_id, key, klen)) {
|
24
23
|
parent->classname = oj_strndup(str, len);
|
25
24
|
parent->clen = len;
|
26
25
|
} else {
|
27
26
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
27
|
+
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
28
28
|
|
29
|
-
if (Qundef == rkey) {
|
30
|
-
if (Yes != pi->options.cache_keys) {
|
31
|
-
if (Yes == pi->options.sym_key) {
|
32
|
-
rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
|
33
|
-
} else {
|
34
|
-
rkey = rb_utf8_str_new(key, klen);
|
35
|
-
}
|
36
|
-
} else if (Yes == pi->options.sym_key) {
|
37
|
-
rkey = oj_sym_intern(key, klen);
|
38
|
-
} else {
|
39
|
-
rkey = oj_str_intern(key, klen);
|
40
|
-
}
|
41
|
-
}
|
42
29
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
43
30
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
44
31
|
|
data/ext/oj/custom.c
CHANGED
@@ -889,12 +889,11 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
889
889
|
///// load functions /////
|
890
890
|
|
891
891
|
static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
892
|
-
const char
|
893
|
-
int
|
894
|
-
Val
|
895
|
-
volatile VALUE rkey = kval->key_val;
|
892
|
+
const char *key = kval->key;
|
893
|
+
int klen = kval->klen;
|
894
|
+
Val parent = stack_peek(&pi->stack);
|
896
895
|
|
897
|
-
if (Qundef ==
|
896
|
+
if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
|
898
897
|
*pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
|
899
898
|
0 == strncmp(pi->options.create_id, key, klen)) {
|
900
899
|
parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
|
@@ -907,15 +906,8 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
907
906
|
}
|
908
907
|
} else {
|
909
908
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
910
|
-
|
909
|
+
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
911
910
|
|
912
|
-
if (Qundef == rkey) {
|
913
|
-
if (Yes == pi->options.sym_key) {
|
914
|
-
rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
|
915
|
-
} else {
|
916
|
-
rkey = rb_utf8_str_new(key, klen);
|
917
|
-
}
|
918
|
-
}
|
919
911
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
920
912
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
921
913
|
|
data/ext/oj/dump.c
CHANGED
@@ -1010,11 +1010,33 @@ static const char digits_table[] = "\
|
|
1010
1010
|
80818283848586878889\
|
1011
1011
|
90919293949596979899";
|
1012
1012
|
|
1013
|
+
char *oj_longlong_to_string(long long num, bool negative, char *buf) {
|
1014
|
+
while (100 <= num) {
|
1015
|
+
unsigned idx = num % 100 * 2;
|
1016
|
+
*buf-- = digits_table[idx + 1];
|
1017
|
+
*buf-- = digits_table[idx];
|
1018
|
+
num /= 100;
|
1019
|
+
}
|
1020
|
+
if (num < 10) {
|
1021
|
+
*buf-- = num + '0';
|
1022
|
+
} else {
|
1023
|
+
*buf-- = digits_table[num * 2 + 1];
|
1024
|
+
*buf-- = digits_table[num * 2];
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
if (negative) {
|
1028
|
+
*buf = '-';
|
1029
|
+
} else {
|
1030
|
+
buf++;
|
1031
|
+
}
|
1032
|
+
return buf;
|
1033
|
+
}
|
1034
|
+
|
1013
1035
|
void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
1014
1036
|
char buf[32];
|
1015
1037
|
char *b = buf + sizeof(buf) - 1;
|
1016
1038
|
long long num = NUM2LL(obj);
|
1017
|
-
|
1039
|
+
bool neg = false;
|
1018
1040
|
size_t cnt = 0;
|
1019
1041
|
bool dump_as_string = false;
|
1020
1042
|
|
@@ -1023,7 +1045,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1023
1045
|
dump_as_string = true;
|
1024
1046
|
}
|
1025
1047
|
if (0 > num) {
|
1026
|
-
neg =
|
1048
|
+
neg = true;
|
1027
1049
|
num = -num;
|
1028
1050
|
}
|
1029
1051
|
*b-- = '\0';
|
@@ -1032,24 +1054,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1032
1054
|
*b-- = '"';
|
1033
1055
|
}
|
1034
1056
|
if (0 < num) {
|
1035
|
-
|
1036
|
-
unsigned idx = num % 100 * 2;
|
1037
|
-
*b-- = digits_table[idx + 1];
|
1038
|
-
*b-- = digits_table[idx];
|
1039
|
-
num /= 100;
|
1040
|
-
}
|
1041
|
-
if (num < 10) {
|
1042
|
-
*b-- = num + '0';
|
1043
|
-
} else {
|
1044
|
-
*b-- = digits_table[num * 2 + 1];
|
1045
|
-
*b-- = digits_table[num * 2];
|
1046
|
-
}
|
1047
|
-
|
1048
|
-
if (neg) {
|
1049
|
-
*b = '-';
|
1050
|
-
} else {
|
1051
|
-
b++;
|
1052
|
-
}
|
1057
|
+
b = oj_longlong_to_string(num, neg, b);
|
1053
1058
|
} else {
|
1054
1059
|
*b = '0';
|
1055
1060
|
}
|
data/ext/oj/dump.h
CHANGED
@@ -93,10 +93,7 @@ inline static void dump_ulong(unsigned long num, Out out) {
|
|
93
93
|
|
94
94
|
*b-- = '\0';
|
95
95
|
if (0 < num) {
|
96
|
-
|
97
|
-
*b = (num % 10) + '0';
|
98
|
-
}
|
99
|
-
b++;
|
96
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
100
97
|
} else {
|
101
98
|
*b = '0';
|
102
99
|
}
|
data/ext/oj/dump_compat.c
CHANGED
@@ -851,13 +851,18 @@ static DumpFunc compat_funcs[] = {
|
|
851
851
|
};
|
852
852
|
|
853
853
|
static void set_state_depth(VALUE state, int depth) {
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
854
|
+
if (0 == rb_const_defined(rb_cObject, rb_intern("JSON"))) {
|
855
|
+
rb_require("oj/json");
|
856
|
+
}
|
857
|
+
{
|
858
|
+
VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
859
|
+
VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
|
860
|
+
VALUE generator = rb_const_get(ext, rb_intern("Generator"));
|
861
|
+
VALUE state_class = rb_const_get(generator, rb_intern("State"));
|
858
862
|
|
859
|
-
|
860
|
-
|
863
|
+
if (state_class == rb_obj_class(state)) {
|
864
|
+
rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
|
865
|
+
}
|
861
866
|
}
|
862
867
|
}
|
863
868
|
|
@@ -865,20 +870,15 @@ void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
865
870
|
int type = rb_type(obj);
|
866
871
|
|
867
872
|
TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
|
873
|
+
// The max_nesting logic is that an empty Array or Hash is assumed to have
|
874
|
+
// content so the max_nesting should fail but a non-collection value is
|
875
|
+
// okay. That means a check for a collectable value is needed before
|
876
|
+
// raising.
|
868
877
|
if (out->opts->dump_opts.max_depth <= depth) {
|
869
|
-
|
870
|
-
// limit is the depth inclusive. If JSON.generate is called then a
|
871
|
-
// NestingError is expected and the limit is inclusive. Worse than
|
872
|
-
// that there are unit tests for both.
|
873
|
-
if (CALLER_DUMP == out->caller) {
|
878
|
+
if (RUBY_T_ARRAY == type || RUBY_T_HASH == type) {
|
874
879
|
if (0 < out->argc) {
|
875
880
|
set_state_depth(*out->argv, depth);
|
876
881
|
}
|
877
|
-
rb_raise(rb_eArgError, "Too deeply nested.");
|
878
|
-
} else if (out->opts->dump_opts.max_depth < depth) {
|
879
|
-
if (0 < out->argc) {
|
880
|
-
set_state_depth(*out->argv, depth - 1);
|
881
|
-
}
|
882
882
|
raise_json_err("Too deeply nested", "NestingError");
|
883
883
|
}
|
884
884
|
}
|
data/ext/oj/extconf.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mkmf'
|
2
4
|
require 'rbconfig'
|
3
5
|
|
@@ -6,7 +8,7 @@ dir_config(extension_name)
|
|
6
8
|
|
7
9
|
parts = RUBY_DESCRIPTION.split(' ')
|
8
10
|
type = parts[0]
|
9
|
-
type = type[4
|
11
|
+
type = type[4..] if type.start_with?('tcs-')
|
10
12
|
is_windows = RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
|
11
13
|
platform = RUBY_PLATFORM
|
12
14
|
version = RUBY_VERSION.split('.')
|
@@ -56,7 +58,7 @@ dflags.each do |k, v|
|
|
56
58
|
end
|
57
59
|
|
58
60
|
$CPPFLAGS += ' -Wall'
|
59
|
-
#puts "*** $CPPFLAGS: #{$CPPFLAGS}"
|
61
|
+
# puts "*** $CPPFLAGS: #{$CPPFLAGS}"
|
60
62
|
# Adding the __attribute__ flag only works with gcc compilers and even then it
|
61
63
|
# does not work to check args with varargs so just remove the check.
|
62
64
|
CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
|
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;
|
@@ -114,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
|
|
114
114
|
char *b = buf + sizeof(buf) - 1;
|
115
115
|
|
116
116
|
*b-- = '\0';
|
117
|
-
|
118
|
-
*b = (num % 10) + '0';
|
119
|
-
}
|
120
|
-
b++;
|
117
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
121
118
|
if ('\0' == *b) {
|
122
119
|
b--;
|
123
120
|
*b = '0';
|
@@ -576,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
|
|
576
573
|
char *h = pi->s; // head
|
577
574
|
char *t = h; // tail
|
578
575
|
|
579
|
-
h++;
|
576
|
+
h++; // skip quote character
|
580
577
|
t++;
|
581
578
|
value = h;
|
582
579
|
for (; '"' != *h; h++, t++) {
|
data/ext/oj/mimic_json.c
CHANGED
@@ -209,7 +209,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
209
209
|
|
210
210
|
oj_out_init(&out);
|
211
211
|
|
212
|
-
out.caller = CALLER_DUMP;
|
213
212
|
copts.escape_mode = JXEsc;
|
214
213
|
copts.mode = CompatMode;
|
215
214
|
|
@@ -368,7 +367,6 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
368
367
|
oj_out_init(&out);
|
369
368
|
|
370
369
|
out.omit_nil = copts->dump_opts.omit_nil;
|
371
|
-
out.caller = CALLER_GENERATE;
|
372
370
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
373
371
|
// it is.
|
374
372
|
copts->dump_opts.nan_dump = RaiseNan;
|
@@ -574,7 +572,6 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
|
|
574
572
|
if (T_HASH != rb_type(ropts)) {
|
575
573
|
rb_raise(rb_eArgError, "options must be a hash.");
|
576
574
|
}
|
577
|
-
|
578
575
|
rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
|
579
576
|
v = rb_hash_lookup(ropts, oj_max_nesting_sym);
|
580
577
|
if (Qtrue == v) {
|
data/ext/oj/oj.c
CHANGED
@@ -123,6 +123,7 @@ static VALUE escape_mode_sym;
|
|
123
123
|
static VALUE integer_range_sym;
|
124
124
|
static VALUE fast_sym;
|
125
125
|
static VALUE float_prec_sym;
|
126
|
+
static VALUE float_format_sym;
|
126
127
|
static VALUE float_sym;
|
127
128
|
static VALUE huge_sym;
|
128
129
|
static VALUE ignore_sym;
|
@@ -232,7 +233,7 @@ struct _options oj_default_options = {
|
|
232
233
|
NULL, // tail
|
233
234
|
{'\0'}, // err
|
234
235
|
},
|
235
|
-
NULL,
|
236
|
+
NULL,
|
236
237
|
};
|
237
238
|
|
238
239
|
/* Document-method: default_options()
|
@@ -267,6 +268,8 @@ struct _options oj_default_options = {
|
|
267
268
|
*seconds portion of time
|
268
269
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
269
270
|
*indicates use Ruby
|
271
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
272
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
270
273
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
|
271
274
|
* - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
|
272
275
|
* - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
|
@@ -378,6 +381,7 @@ static VALUE get_def_opts(VALUE self) {
|
|
378
381
|
oj_safe_sym,
|
379
382
|
(Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
|
380
383
|
rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
|
384
|
+
rb_hash_aset(opts, float_format_sym, rb_str_new_cstr(oj_default_options.float_fmt));
|
381
385
|
rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
|
382
386
|
rb_hash_aset(
|
383
387
|
opts,
|
@@ -519,6 +523,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
519
523
|
*load.
|
520
524
|
* - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
|
521
525
|
*seconds portion of time.
|
526
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
527
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
522
528
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
523
529
|
*indicates use Ruby.
|
524
530
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
|
@@ -617,7 +623,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
617
623
|
if (set_yesno_options(k, v, copts)) {
|
618
624
|
return ST_CONTINUE;
|
619
625
|
}
|
620
|
-
|
621
626
|
if (oj_indent_sym == k) {
|
622
627
|
switch (rb_type(v)) {
|
623
628
|
case T_NIL:
|
@@ -757,7 +762,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
757
762
|
if (Qnil == v) {
|
758
763
|
return ST_CONTINUE;
|
759
764
|
}
|
760
|
-
|
761
765
|
copts->compat_bigdec = (Qtrue == v);
|
762
766
|
} else if (oj_decimal_class_sym == k) {
|
763
767
|
if (rb_cFloat == v) {
|
@@ -949,6 +953,25 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
949
953
|
return ST_CONTINUE;
|
950
954
|
}
|
951
955
|
copts->sym_key = (Qtrue == v) ? Yes : No;
|
956
|
+
|
957
|
+
} else if (oj_max_nesting_sym == k) {
|
958
|
+
if (Qtrue == v) {
|
959
|
+
copts->dump_opts.max_depth = 100;
|
960
|
+
} else if (Qfalse == v || Qnil == v) {
|
961
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
962
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
963
|
+
copts->dump_opts.max_depth = NUM2INT(v);
|
964
|
+
if (0 >= copts->dump_opts.max_depth) {
|
965
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
966
|
+
}
|
967
|
+
}
|
968
|
+
} else if (float_format_sym == k) {
|
969
|
+
rb_check_type(v, T_STRING);
|
970
|
+
if (6 < (int)RSTRING_LEN(v)) {
|
971
|
+
rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
|
972
|
+
}
|
973
|
+
strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
974
|
+
copts->float_fmt[RSTRING_LEN(v)] = '\0';
|
952
975
|
}
|
953
976
|
return ST_CONTINUE;
|
954
977
|
}
|
@@ -957,7 +980,6 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
957
980
|
if (T_HASH != rb_type(ropts)) {
|
958
981
|
return;
|
959
982
|
}
|
960
|
-
|
961
983
|
rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
|
962
984
|
oj_parse_opt_match_string(&copts->str_rx, ropts);
|
963
985
|
|
@@ -1294,9 +1316,8 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1294
1316
|
|
1295
1317
|
oj_out_init(arg.out);
|
1296
1318
|
|
1297
|
-
arg.out->omit_nil
|
1319
|
+
arg.out->omit_nil = copts.dump_opts.omit_nil;
|
1298
1320
|
arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
|
1299
|
-
arg.out->caller = CALLER_DUMP;
|
1300
1321
|
|
1301
1322
|
return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
|
1302
1323
|
}
|
@@ -1308,8 +1329,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1308
1329
|
* will be called. The mode is set to :compat.
|
1309
1330
|
* - *obj* [_Object_] Object to serialize as an JSON document String
|
1310
1331
|
* - *options* [_Hash_]
|
1311
|
-
* - *:max_nesting* [_boolean_] It true nesting is limited to 100.
|
1312
|
-
*
|
1332
|
+
* - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100. If a Fixnum nesting
|
1333
|
+
* is set to the provided value. The option to detect circular references is available but is not
|
1334
|
+
* compatible with the json gem., default is false or unlimited.
|
1313
1335
|
* - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
|
1314
1336
|
* used as appropriate, default is true.
|
1315
1337
|
* - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
|
@@ -1343,7 +1365,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
1343
1365
|
|
1344
1366
|
oj_out_init(&out);
|
1345
1367
|
|
1346
|
-
out.omit_nil
|
1368
|
+
out.omit_nil = copts.dump_opts.omit_nil;
|
1347
1369
|
out.omit_null_byte = copts.dump_opts.omit_null_byte;
|
1348
1370
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
1349
1371
|
// it is.
|
@@ -1929,6 +1951,8 @@ void Init_oj(void) {
|
|
1929
1951
|
rb_gc_register_address(&integer_range_sym);
|
1930
1952
|
fast_sym = ID2SYM(rb_intern("fast"));
|
1931
1953
|
rb_gc_register_address(&fast_sym);
|
1954
|
+
float_format_sym = ID2SYM(rb_intern("float_format"));
|
1955
|
+
rb_gc_register_address(&float_format_sym);
|
1932
1956
|
float_prec_sym = ID2SYM(rb_intern("float_precision"));
|
1933
1957
|
rb_gc_register_address(&float_prec_sym);
|
1934
1958
|
float_sym = ID2SYM(rb_intern("float"));
|
data/ext/oj/oj.h
CHANGED
@@ -103,13 +103,6 @@ typedef enum {
|
|
103
103
|
FILE_IO = 'f',
|
104
104
|
} StreamWriterType;
|
105
105
|
|
106
|
-
typedef enum {
|
107
|
-
CALLER_DUMP = 'd',
|
108
|
-
CALLER_TO_JSON = 't',
|
109
|
-
CALLER_GENERATE = 'g',
|
110
|
-
// Add the fast versions if necessary. Maybe unparse as well if needed.
|
111
|
-
} DumpCaller;
|
112
|
-
|
113
106
|
typedef struct _dumpOpts {
|
114
107
|
bool use;
|
115
108
|
char indent_str[16];
|
@@ -188,23 +181,22 @@ typedef struct _rOptTable {
|
|
188
181
|
} *ROptTable;
|
189
182
|
|
190
183
|
typedef struct _out {
|
191
|
-
char
|
192
|
-
char
|
193
|
-
char
|
194
|
-
char
|
195
|
-
Cache8
|
196
|
-
slot_t
|
197
|
-
int
|
198
|
-
int
|
199
|
-
Options
|
200
|
-
uint32_t
|
201
|
-
bool
|
202
|
-
bool
|
203
|
-
bool
|
204
|
-
int
|
205
|
-
VALUE
|
206
|
-
|
207
|
-
ROptTable ropts;
|
184
|
+
char stack_buffer[4096];
|
185
|
+
char *buf;
|
186
|
+
char *end;
|
187
|
+
char *cur;
|
188
|
+
Cache8 circ_cache;
|
189
|
+
slot_t circ_cnt;
|
190
|
+
int indent;
|
191
|
+
int depth; // used by dump_hash
|
192
|
+
Options opts;
|
193
|
+
uint32_t hash_cnt;
|
194
|
+
bool allocated;
|
195
|
+
bool omit_nil;
|
196
|
+
bool omit_null_byte;
|
197
|
+
int argc;
|
198
|
+
VALUE *argv;
|
199
|
+
ROptTable ropts;
|
208
200
|
} *Out;
|
209
201
|
|
210
202
|
typedef struct _strWriter {
|
@@ -262,12 +254,13 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
|
|
262
254
|
extern bool oj_hash_has_key(VALUE hash, VALUE key);
|
263
255
|
extern void oj_parse_options(VALUE ropts, Options copts);
|
264
256
|
|
265
|
-
extern void
|
266
|
-
extern void
|
267
|
-
extern void
|
268
|
-
extern void
|
269
|
-
extern void
|
270
|
-
extern void
|
257
|
+
extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
|
258
|
+
extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
|
259
|
+
extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
|
260
|
+
extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
|
261
|
+
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
262
|
+
extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
|
263
|
+
extern char *oj_longlong_to_string(long long num, bool negative, char *buf);
|
271
264
|
|
272
265
|
extern void oj_str_writer_push_key(StrWriter sw, const char *key);
|
273
266
|
extern void oj_str_writer_push_object(StrWriter sw, const char *key);
|
data/ext/oj/parse.c
CHANGED
@@ -426,6 +426,7 @@ static void read_num(ParseInfo pi) {
|
|
426
426
|
struct _numInfo ni;
|
427
427
|
Val parent = stack_peek(&pi->stack);
|
428
428
|
|
429
|
+
ni.pi = pi;
|
429
430
|
ni.str = pi->cur;
|
430
431
|
ni.i = 0;
|
431
432
|
ni.num = 0;
|
@@ -709,10 +710,7 @@ void oj_parse2(ParseInfo pi) {
|
|
709
710
|
case '[': array_start(pi); break;
|
710
711
|
case ']': array_end(pi); break;
|
711
712
|
case ',': comma(pi); break;
|
712
|
-
case '"':
|
713
|
-
read_str(pi);
|
714
|
-
break;
|
715
|
-
// case '+':
|
713
|
+
case '"': read_str(pi); break;
|
716
714
|
case '+':
|
717
715
|
if (CompatMode == pi->options.mode) {
|
718
716
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
@@ -877,7 +875,7 @@ oj_num_as_value(NumInfo ni) {
|
|
877
875
|
double d = strtod(ni->str, &end);
|
878
876
|
|
879
877
|
if ((long)ni->len != (long)(end - ni->str)) {
|
880
|
-
rb_raise(
|
878
|
+
rb_raise(ni->pi->err_class, "Invalid float");
|
881
879
|
}
|
882
880
|
rnum = rb_float_new(d);
|
883
881
|
}
|
data/ext/oj/parse.h
CHANGED
@@ -16,22 +16,24 @@
|
|
16
16
|
#include "val_stack.h"
|
17
17
|
|
18
18
|
struct _rxClass;
|
19
|
+
struct _parseInfo;
|
19
20
|
|
20
21
|
typedef struct _numInfo {
|
21
|
-
int64_t
|
22
|
-
int64_t
|
23
|
-
int64_t
|
24
|
-
int64_t
|
25
|
-
const char
|
26
|
-
size_t
|
27
|
-
long
|
28
|
-
|
29
|
-
int
|
30
|
-
int
|
31
|
-
int
|
32
|
-
int
|
33
|
-
int
|
34
|
-
int
|
22
|
+
int64_t i;
|
23
|
+
int64_t num;
|
24
|
+
int64_t div;
|
25
|
+
int64_t di;
|
26
|
+
const char *str;
|
27
|
+
size_t len;
|
28
|
+
long exp;
|
29
|
+
struct _parseInfo *pi;
|
30
|
+
int big;
|
31
|
+
int infinity;
|
32
|
+
int nan;
|
33
|
+
int neg;
|
34
|
+
int has_exp;
|
35
|
+
int no_big;
|
36
|
+
int bigdec_load;
|
35
37
|
} *NumInfo;
|
36
38
|
|
37
39
|
typedef struct _parseInfo {
|
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/rails.c
CHANGED
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
|
|
@@ -164,7 +164,6 @@ static VALUE partial_io_cb(VALUE rbuf) {
|
|
164
164
|
}
|
165
165
|
str = StringValuePtr(rstr);
|
166
166
|
cnt = RSTRING_LEN(rstr);
|
167
|
-
// printf("*** partial read %lu bytes, str: '%s'\n", cnt, str);
|
168
167
|
strcpy(reader->tail, str);
|
169
168
|
reader->read_end = reader->tail + cnt;
|
170
169
|
|
@@ -185,7 +184,6 @@ static VALUE io_cb(VALUE rbuf) {
|
|
185
184
|
}
|
186
185
|
str = StringValuePtr(rstr);
|
187
186
|
cnt = RSTRING_LEN(rstr);
|
188
|
-
// printf("*** read %lu bytes, str: '%s'\n", cnt, str);
|
189
187
|
strcpy(reader->tail, str);
|
190
188
|
reader->read_end = reader->tail + cnt;
|
191
189
|
|
data/ext/oj/saj.c
CHANGED
@@ -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/string_writer.c
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/time'
|
2
4
|
|
3
5
|
module Oj
|
4
|
-
|
5
6
|
# Exists only to handle the ActiveSupport::TimeWithZone.
|
6
7
|
class ActiveSupportHelper
|
7
|
-
|
8
8
|
def self.createTimeWithZone(utc, zone)
|
9
9
|
ActiveSupport::TimeWithZone.new(utc - utc.gmt_offset, ActiveSupport::TimeZone[zone])
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
12
|
end
|
14
13
|
|
15
14
|
Oj.register_odd(ActiveSupport::TimeWithZone, Oj::ActiveSupportHelper, :createTimeWithZone, :utc, 'time_zone.name')
|
data/lib/oj/json.rb
CHANGED
@@ -1,178 +1,188 @@
|
|
1
1
|
require 'ostruct'
|
2
2
|
require 'oj/state'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
4
|
+
if defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
5
|
+
# There are enough people that try to use both the json gen and oj in mimic
|
6
|
+
# mode so don't display the warning.
|
7
|
+
|
8
|
+
# warn "WARNING: oj/json is a compatability shim used by Oj. Requiring the file explicitly is not recommended."
|
9
|
+
end
|
10
|
+
|
11
|
+
unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
12
|
+
module JSON
|
13
|
+
NaN = 0.0/0.0 unless defined?(::JSON::NaN)
|
14
|
+
Infinity = 1.0/0.0 unless defined?(::JSON::Infinity)
|
15
|
+
MinusInfinity = -1.0/0.0 unless defined?(::JSON::MinusInfinity)
|
16
|
+
# Taken from the unit test. Note that items like check_circular? are not
|
17
|
+
# present.
|
18
|
+
PRETTY_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
19
|
+
:allow_nan => false,
|
20
|
+
:array_nl => "\n",
|
21
|
+
:ascii_only => false,
|
22
|
+
:buffer_initial_length => 1024,
|
23
|
+
:depth => 0,
|
24
|
+
:indent => " ",
|
25
|
+
:max_nesting => 100,
|
26
|
+
:object_nl => "\n",
|
27
|
+
:space => " ",
|
28
|
+
:space_before => "",
|
29
|
+
}) unless defined?(::JSON::PRETTY_STATE_PROTOTYPE)
|
30
|
+
SAFE_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
31
|
+
:allow_nan => false,
|
32
|
+
:array_nl => "",
|
33
|
+
:ascii_only => false,
|
34
|
+
:buffer_initial_length => 1024,
|
35
|
+
:depth => 0,
|
36
|
+
:indent => "",
|
37
|
+
:max_nesting => 100,
|
38
|
+
:object_nl => "",
|
39
|
+
:space => "",
|
40
|
+
:space_before => "",
|
41
|
+
}) unless defined?(::JSON::SAFE_STATE_PROTOTYPE)
|
42
|
+
FAST_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
43
|
+
:allow_nan => false,
|
44
|
+
:array_nl => "",
|
45
|
+
:ascii_only => false,
|
46
|
+
:buffer_initial_length => 1024,
|
47
|
+
:depth => 0,
|
48
|
+
:indent => "",
|
49
|
+
:max_nesting => 0,
|
50
|
+
:object_nl => "",
|
51
|
+
:space => "",
|
52
|
+
:space_before => "",
|
53
|
+
}) unless defined?(::JSON::FAST_STATE_PROTOTYPE)
|
54
|
+
|
55
|
+
def self.dump_default_options
|
56
|
+
Oj::MimicDumpOption.new
|
55
57
|
end
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
def self.dump_default_options=(h)
|
60
|
+
m = Oj::MimicDumpOption.new
|
61
|
+
h.each do |k, v|
|
62
|
+
m[k] = v
|
63
|
+
end
|
64
|
+
end
|
61
65
|
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
def self.parser=(p)
|
67
|
+
@@parser = p
|
68
|
+
end
|
65
69
|
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
def self.parser()
|
71
|
+
@@parser
|
72
|
+
end
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
def self.generator=(g)
|
75
|
+
@@generator = g
|
76
|
+
end
|
73
77
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
raise TypeError.new("already initialized") unless @source.nil?
|
78
|
+
def self.generator()
|
79
|
+
@@generator
|
80
|
+
end
|
78
81
|
|
79
|
-
|
80
|
-
|
82
|
+
module Ext
|
83
|
+
class Parser
|
84
|
+
def initialize(src)
|
85
|
+
raise TypeError.new("already initialized") unless @source.nil?
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
@source = src
|
88
|
+
end
|
84
89
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
def parse()
|
89
|
-
raise TypeError.new("already initialized") if @source.nil?
|
90
|
+
def source()
|
91
|
+
raise TypeError.new("already initialized") if @source.nil?
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end # Parser
|
95
|
-
end # Ext
|
96
|
-
|
97
|
-
State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
|
98
|
-
|
99
|
-
begin
|
100
|
-
send(:remove_const, :Parser)
|
101
|
-
rescue
|
102
|
-
end
|
103
|
-
Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
|
104
|
-
self.parser = ::JSON::Ext::Parser
|
105
|
-
self.generator = ::JSON::Ext::Generator
|
106
|
-
|
107
|
-
# Taken directly from the json gem. Shamelessly copied. It is similar in
|
108
|
-
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
109
|
-
class GenericObject < OpenStruct
|
110
|
-
class << self
|
111
|
-
alias [] new
|
112
|
-
|
113
|
-
def json_creatable?
|
114
|
-
@json_creatable
|
115
|
-
end
|
93
|
+
@source
|
94
|
+
end
|
116
95
|
|
117
|
-
|
96
|
+
def parse()
|
97
|
+
raise TypeError.new("already initialized") if @source.nil?
|
118
98
|
|
119
|
-
|
120
|
-
|
121
|
-
data.delete JSON.create_id
|
122
|
-
self[data]
|
123
|
-
end
|
99
|
+
JSON.parse(@source)
|
100
|
+
end
|
124
101
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
102
|
+
end # Parser
|
103
|
+
end # Ext
|
104
|
+
|
105
|
+
State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
|
106
|
+
|
107
|
+
begin
|
108
|
+
send(:remove_const, :Parser)
|
109
|
+
rescue
|
110
|
+
# ignore and move on
|
111
|
+
end
|
112
|
+
Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
|
113
|
+
self.parser = ::JSON::Ext::Parser
|
114
|
+
self.generator = ::JSON::Ext::Generator
|
115
|
+
|
116
|
+
# Taken directly from the json gem. Shamelessly copied. It is similar in
|
117
|
+
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
118
|
+
class GenericObject < OpenStruct
|
119
|
+
class << self
|
120
|
+
alias [] new
|
121
|
+
|
122
|
+
def json_creatable?
|
123
|
+
@json_creatable
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_writer :json_creatable
|
127
|
+
|
128
|
+
def json_create(data)
|
129
|
+
data = data.dup
|
130
|
+
data.delete JSON.create_id
|
131
|
+
self[data]
|
132
|
+
end
|
133
|
+
|
134
|
+
def from_hash(object)
|
135
|
+
case
|
136
|
+
when object.respond_to?(:to_hash)
|
137
|
+
result = new
|
138
|
+
object.to_hash.each do |key, value|
|
139
|
+
result[key] = from_hash(value)
|
140
|
+
end
|
141
|
+
result
|
142
|
+
when object.respond_to?(:to_ary)
|
143
|
+
object.to_ary.map { |a| from_hash(a) }
|
144
|
+
else
|
145
|
+
object
|
131
146
|
end
|
132
|
-
result
|
133
|
-
when object.respond_to?(:to_ary)
|
134
|
-
object.to_ary.map { |a| from_hash(a) }
|
135
|
-
else
|
136
|
-
object
|
137
147
|
end
|
138
|
-
end
|
139
148
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
149
|
+
def load(source, proc = nil, opts = {})
|
150
|
+
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
|
151
|
+
result.nil? ? new : result
|
152
|
+
end
|
144
153
|
|
145
|
-
|
146
|
-
|
147
|
-
|
154
|
+
def dump(obj, *args)
|
155
|
+
::JSON.dump(obj, *args)
|
156
|
+
end
|
148
157
|
|
149
|
-
|
158
|
+
end # self
|
150
159
|
|
151
|
-
|
160
|
+
self.json_creatable = false
|
152
161
|
|
153
|
-
|
154
|
-
|
155
|
-
|
162
|
+
def to_hash
|
163
|
+
table
|
164
|
+
end
|
156
165
|
|
157
|
-
|
158
|
-
|
159
|
-
|
166
|
+
def [](name)
|
167
|
+
__send__(name)
|
168
|
+
end unless method_defined?(:[])
|
160
169
|
|
161
|
-
|
162
|
-
|
163
|
-
|
170
|
+
def []=(name, value)
|
171
|
+
__send__("#{name}=", value)
|
172
|
+
end unless method_defined?(:[]=)
|
164
173
|
|
165
|
-
|
166
|
-
|
167
|
-
|
174
|
+
def |(other)
|
175
|
+
self.class[other.to_hash.merge(to_hash)]
|
176
|
+
end
|
168
177
|
|
169
|
-
|
170
|
-
|
171
|
-
|
178
|
+
def as_json(*)
|
179
|
+
{ JSON.create_id => self.class.name }.merge to_hash
|
180
|
+
end
|
172
181
|
|
173
|
-
|
174
|
-
|
182
|
+
def to_json(*a)
|
183
|
+
as_json.to_json(*a)
|
184
|
+
end
|
175
185
|
end
|
176
|
-
|
177
|
-
|
178
|
-
end
|
186
|
+
|
187
|
+
end # JSON
|
188
|
+
end
|
data/lib/oj/mimic.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
1
3
|
require 'bigdecimal'
|
2
4
|
begin
|
3
5
|
require 'ostruct'
|
@@ -266,7 +268,7 @@ module Oj
|
|
266
268
|
end
|
267
269
|
end
|
268
270
|
def self.json_create(h)
|
269
|
-
if usec = h.delete('u')
|
271
|
+
if (usec = h.delete('u'))
|
270
272
|
h['n'] = usec * 1000
|
271
273
|
end
|
272
274
|
if instance_methods.include?(:tv_nsec)
|
data/lib/oj/version.rb
CHANGED
data/test/foo.rb
CHANGED
@@ -5,11 +5,10 @@ $LOAD_PATH << '.'
|
|
5
5
|
$LOAD_PATH << File.join(__dir__, '../lib')
|
6
6
|
$LOAD_PATH << File.join(__dir__, '../ext')
|
7
7
|
|
8
|
+
require 'json'
|
8
9
|
require 'oj'
|
9
|
-
|
10
|
-
GC.stress = true
|
10
|
+
require 'oj/json'
|
11
11
|
|
12
12
|
Oj.mimic_JSON
|
13
13
|
|
14
|
-
|
15
|
-
pp JSON('{ "a": 1, "b": 2 }', :object_class => JSON::GenericObject)
|
14
|
+
JSON.parse("[]")
|
@@ -128,8 +128,10 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
128
128
|
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
129
129
|
assert_equal too_deep, JSON.dump(eval(too_deep))
|
130
130
|
assert_kind_of String, Marshal.dump(eval(too_deep))
|
131
|
-
|
132
|
-
|
131
|
+
if RUBY_ENGINE != 'truffleruby'
|
132
|
+
assert_raise(ArgumentError) { JSON.dump(eval(too_deep), 100) }
|
133
|
+
assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
|
134
|
+
end
|
133
135
|
assert_equal too_deep, JSON.dump(eval(too_deep), 101)
|
134
136
|
assert_kind_of String, Marshal.dump(eval(too_deep), 101)
|
135
137
|
output = StringIO.new
|
@@ -352,7 +352,7 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
352
352
|
too_deep_ary = eval too_deep
|
353
353
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary }
|
354
354
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
|
355
|
-
ok = JSON.generate too_deep_ary, :max_nesting =>
|
355
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 102
|
356
356
|
assert_equal too_deep, ok
|
357
357
|
ok = JSON.generate too_deep_ary, :max_nesting => nil
|
358
358
|
assert_equal too_deep, ok
|
data/test/perf_dump.rb
CHANGED
@@ -24,11 +24,11 @@ opts.parse(ARGV)
|
|
24
24
|
@obj = {
|
25
25
|
'a' => 'Alpha', # string
|
26
26
|
'b' => true, # boolean
|
27
|
-
'c' =>
|
28
|
-
'd' => [ true, [false, [-
|
27
|
+
'c' => 12_345, # number
|
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
@@ -467,10 +467,28 @@ class CompatJuice < Minitest::Test
|
|
467
467
|
end
|
468
468
|
|
469
469
|
def test_arg_passing
|
470
|
-
json = Oj.to_json(Argy.new(), :max_nesting=> 40)
|
470
|
+
json = Oj.to_json(Argy.new(), :max_nesting => 40)
|
471
471
|
assert_equal(%|{"args":"[{:max_nesting=>40}]"}|, json)
|
472
472
|
end
|
473
473
|
|
474
|
+
def test_max_nesting
|
475
|
+
assert_raises() { Oj.to_json([[[[[]]]]], :max_nesting => 3) }
|
476
|
+
assert_raises() { Oj.dump([[[[[]]]]], :max_nesting => 3, :mode=>:compat) }
|
477
|
+
|
478
|
+
assert_raises() { Oj.to_json([[]], :max_nesting => 1) }
|
479
|
+
assert_equal('[[]]', Oj.to_json([[]], :max_nesting => 2))
|
480
|
+
|
481
|
+
assert_raises() { Oj.dump([[]], :max_nesting => 1, :mode=>:compat) }
|
482
|
+
assert_equal('[[]]', Oj.dump([[]], :max_nesting => 2, :mode=>:compat))
|
483
|
+
|
484
|
+
assert_raises() { Oj.to_json([[3]], :max_nesting => 1) }
|
485
|
+
assert_equal('[[3]]', Oj.to_json([[3]], :max_nesting => 2))
|
486
|
+
|
487
|
+
assert_raises() { Oj.dump([[3]], :max_nesting => 1, :mode=>:compat) }
|
488
|
+
assert_equal('[[3]]', Oj.dump([[3]], :max_nesting => 2, :mode=>:compat))
|
489
|
+
|
490
|
+
end
|
491
|
+
|
474
492
|
def test_bad_unicode
|
475
493
|
assert_raises() { Oj.to_json("\xE4xy") }
|
476
494
|
end
|
data/test/test_parser_debug.rb
CHANGED
data/test/test_various.rb
CHANGED
@@ -105,6 +105,7 @@ class Juice < Minitest::Test
|
|
105
105
|
allow_gc: false,
|
106
106
|
quirks_mode: false,
|
107
107
|
allow_invalid_unicode: true,
|
108
|
+
float_format: '%0.13g',
|
108
109
|
float_precision: 13,
|
109
110
|
mode: :strict,
|
110
111
|
escape_mode: :ascii,
|
@@ -416,6 +417,11 @@ class Juice < Minitest::Test
|
|
416
417
|
}
|
417
418
|
end
|
418
419
|
|
420
|
+
def test_dump_float
|
421
|
+
json = Oj.dump(1.23e-2, :mode => :null, :float_format => '%0.4f')
|
422
|
+
assert_equal('0.0123', json)
|
423
|
+
end
|
424
|
+
|
419
425
|
# Class
|
420
426
|
def test_class_null
|
421
427
|
json = Oj.dump(Juice, :mode => :null)
|
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.
|
4
|
+
version: 3.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|