oj 2.18.5 → 3.0.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/README.md +33 -226
- data/ext/oj/circarray.c +0 -25
- data/ext/oj/circarray.h +0 -25
- data/ext/oj/code.c +227 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +126 -38
- data/ext/oj/custom.c +1097 -0
- data/ext/oj/dump.c +658 -2376
- data/ext/oj/dump.h +92 -0
- data/ext/oj/dump_compat.c +937 -0
- data/ext/oj/dump_leaf.c +254 -0
- data/ext/oj/dump_object.c +810 -0
- data/ext/oj/dump_rails.c +329 -0
- data/ext/oj/dump_strict.c +416 -0
- data/ext/oj/err.c +0 -25
- data/ext/oj/err.h +8 -2
- data/ext/oj/fast.c +24 -24
- data/ext/oj/mimic_json.c +817 -0
- data/ext/oj/mimic_rails.c +806 -0
- data/ext/oj/mimic_rails.h +17 -0
- data/ext/oj/object.c +18 -72
- data/ext/oj/odd.c +0 -25
- data/ext/oj/odd.h +2 -27
- data/ext/oj/oj.c +655 -1503
- data/ext/oj/oj.h +93 -40
- data/ext/oj/parse.c +99 -46
- data/ext/oj/parse.h +12 -26
- data/ext/oj/reader.c +1 -25
- data/ext/oj/reader.h +3 -25
- data/ext/oj/resolve.c +9 -11
- data/ext/oj/resolve.h +2 -2
- data/ext/oj/rxclass.c +133 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +4 -25
- data/ext/oj/scp.c +3 -25
- data/ext/oj/sparse.c +89 -13
- data/ext/oj/stream_writer.c +301 -0
- data/ext/oj/strict.c +4 -27
- data/ext/oj/string_writer.c +480 -0
- data/ext/oj/val_stack.h +6 -2
- data/lib/oj.rb +1 -23
- data/lib/oj/easy_hash.rb +12 -4
- data/lib/oj/json.rb +172 -0
- data/lib/oj/mimic.rb +123 -18
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +1 -1
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +79 -0
- data/pages/Modes.md +140 -0
- data/pages/Options.md +250 -0
- data/pages/Rails.md +60 -0
- data/pages/Security.md +20 -0
- data/test/activesupport4/decoding_test.rb +105 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +483 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +143 -0
- data/test/json_gem/json_encoding_test.rb +109 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/perf_compat.rb +30 -28
- data/test/perf_object.rb +1 -1
- data/test/perf_strict.rb +18 -1
- data/test/sample.rb +0 -1
- data/test/test_compat.rb +169 -93
- data/test/test_custom.rb +355 -0
- data/test/test_file.rb +0 -8
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +268 -3
- data/test/test_scp.rb +22 -1
- data/test/test_strict.rb +160 -4
- data/test/test_various.rb +52 -620
- data/test/tests.rb +14 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- metadata +89 -47
- data/test/activesupport_datetime_test.rb +0 -23
- data/test/bug.rb +0 -51
- data/test/bug2.rb +0 -10
- data/test/bug3.rb +0 -46
- data/test/bug_fast.rb +0 -32
- data/test/bug_load.rb +0 -24
- data/test/crash.rb +0 -111
- data/test/curl/curl_oj.rb +0 -46
- data/test/curl/get_oj.rb +0 -24
- data/test/curl/just_curl.rb +0 -31
- data/test/curl/just_oj.rb +0 -51
- data/test/example.rb +0 -11
- data/test/foo.rb +0 -24
- data/test/io.rb +0 -48
- data/test/isolated/test_mimic_rails_datetime.rb +0 -27
- data/test/mod.rb +0 -16
- data/test/rails.rb +0 -50
- data/test/russian.rb +0 -18
- data/test/struct.rb +0 -29
- data/test/test_serializer.rb +0 -59
- data/test/write_timebars.rb +0 -31
data/ext/oj/code.h
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
/* code.h
|
2
|
+
* Copyright (c) 2017, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef __OJ_CODE_H__
|
7
|
+
#define __OJ_CODE_H__
|
8
|
+
|
9
|
+
#include <ruby.h>
|
10
|
+
|
11
|
+
#include "oj.h"
|
12
|
+
|
13
|
+
typedef void (*EncodeFunc)(VALUE obj, int depth, Out out);
|
14
|
+
typedef VALUE (*DecodeFunc)(VALUE clas, VALUE args);
|
15
|
+
|
16
|
+
typedef struct _Code {
|
17
|
+
const char *name;
|
18
|
+
VALUE clas;
|
19
|
+
EncodeFunc encode;
|
20
|
+
DecodeFunc decode;
|
21
|
+
bool active; // For compat mode.
|
22
|
+
} *Code;
|
23
|
+
|
24
|
+
// Used by encode functions.
|
25
|
+
typedef struct _Attr {
|
26
|
+
const char *name;
|
27
|
+
int len;
|
28
|
+
VALUE value;
|
29
|
+
long num;
|
30
|
+
VALUE time;
|
31
|
+
} *Attr;
|
32
|
+
|
33
|
+
extern bool oj_code_dump(Code codes, VALUE obj, int depth, Out out);
|
34
|
+
extern VALUE oj_code_load(Code codes, VALUE clas, VALUE args);
|
35
|
+
extern void oj_code_set_active(Code codes, VALUE clas, bool active);
|
36
|
+
extern bool oj_code_has(Code codes, VALUE clas, bool encode);
|
37
|
+
|
38
|
+
extern void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out);
|
39
|
+
|
40
|
+
#endif /* __OJ_CODE_H__ */
|
data/ext/oj/compat.c
CHANGED
@@ -1,31 +1,6 @@
|
|
1
1
|
/* compat.c
|
2
2
|
* Copyright (c) 2012, Peter Ohler
|
3
3
|
* All rights reserved.
|
4
|
-
*
|
5
|
-
* Redistribution and use in source and binary forms, with or without
|
6
|
-
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
8
|
-
* - Redistributions of source code must retain the above copyright notice, this
|
9
|
-
* list of conditions and the following disclaimer.
|
10
|
-
*
|
11
|
-
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
|
-
* this list of conditions and the following disclaimer in the documentation
|
13
|
-
* and/or other materials provided with the distribution.
|
14
|
-
*
|
15
|
-
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
|
-
* used to endorse or promote products derived from this software without
|
17
|
-
* specific prior written permission.
|
18
|
-
*
|
19
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
-
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
-
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
-
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
-
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
-
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
-
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
-
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
4
|
*/
|
30
5
|
|
31
6
|
#include <stdio.h>
|
@@ -45,10 +20,12 @@ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *o
|
|
45
20
|
volatile VALUE rkey = kval->key_val;
|
46
21
|
|
47
22
|
if (Qundef == rkey &&
|
48
|
-
|
23
|
+
Yes == pi->options.create_ok &&
|
24
|
+
NULL != pi->options.create_id &&
|
49
25
|
*pi->options.create_id == *key &&
|
50
26
|
(int)pi->options.create_id_len == klen &&
|
51
27
|
0 == strncmp(pi->options.create_id, key, klen)) {
|
28
|
+
|
52
29
|
parent->classname = oj_strndup(str, len);
|
53
30
|
parent->clen = len;
|
54
31
|
} else {
|
@@ -62,20 +39,51 @@ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *o
|
|
62
39
|
rkey = rb_str_intern(rkey);
|
63
40
|
}
|
64
41
|
}
|
65
|
-
|
42
|
+
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
43
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
44
|
+
|
45
|
+
if (Qnil != clas) {
|
46
|
+
rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
if (rb_cHash != rb_obj_class(parent->val)) {
|
50
|
+
// The rb_hash_set would still work but the unit tests for the
|
51
|
+
// json gem require the less efficient []= method be called to set
|
52
|
+
// values. Even using the store method to set the values will fail
|
53
|
+
// the unit tests.
|
54
|
+
rb_funcall(parent->val, rb_intern("[]="), 2, rkey, rstr);
|
55
|
+
} else {
|
56
|
+
rb_hash_aset(parent->val, rkey, rstr);
|
57
|
+
}
|
66
58
|
}
|
67
59
|
}
|
68
60
|
|
61
|
+
static VALUE
|
62
|
+
start_hash(ParseInfo pi) {
|
63
|
+
volatile VALUE h;
|
64
|
+
|
65
|
+
if (Qnil != pi->options.hash_class) {
|
66
|
+
h = rb_class_new_instance(0, NULL, pi->options.hash_class);
|
67
|
+
} else {
|
68
|
+
h = rb_hash_new();
|
69
|
+
}
|
70
|
+
return h;
|
71
|
+
}
|
72
|
+
|
69
73
|
static void
|
70
74
|
end_hash(struct _ParseInfo *pi) {
|
71
75
|
Val parent = stack_peek(&pi->stack);
|
72
76
|
|
73
77
|
if (0 != parent->classname) {
|
74
|
-
VALUE clas;
|
78
|
+
volatile VALUE clas;
|
75
79
|
|
76
|
-
clas = oj_name2class(pi, parent->classname, parent->clen, 0);
|
80
|
+
clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
|
77
81
|
if (Qundef != clas) { // else an error
|
78
|
-
|
82
|
+
ID creatable = rb_intern("json_creatable?");
|
83
|
+
|
84
|
+
if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
|
85
|
+
parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
|
86
|
+
}
|
79
87
|
}
|
80
88
|
if (0 != parent->classname) {
|
81
89
|
xfree((char*)parent->classname);
|
@@ -98,6 +106,22 @@ calc_hash_key(ParseInfo pi, Val parent) {
|
|
98
106
|
return rkey;
|
99
107
|
}
|
100
108
|
|
109
|
+
static void
|
110
|
+
add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
111
|
+
volatile VALUE rstr = rb_str_new(str, len);
|
112
|
+
|
113
|
+
rstr = oj_encode(rstr);
|
114
|
+
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
115
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
116
|
+
|
117
|
+
if (Qnil != clas) {
|
118
|
+
pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
pi->stack.head->val = rstr;
|
123
|
+
}
|
124
|
+
|
101
125
|
static void
|
102
126
|
add_num(ParseInfo pi, NumInfo ni) {
|
103
127
|
pi->stack.head->val = oj_num_as_value(ni);
|
@@ -105,22 +129,80 @@ add_num(ParseInfo pi, NumInfo ni) {
|
|
105
129
|
|
106
130
|
static void
|
107
131
|
hash_set_num(struct _ParseInfo *pi, Val parent, NumInfo ni) {
|
108
|
-
|
132
|
+
if (!oj_use_hash_alt && rb_cHash != rb_obj_class(parent->val)) {
|
133
|
+
// The rb_hash_set would still work but the unit tests for the
|
134
|
+
// json gem require the less efficient []= method be called to set
|
135
|
+
// values. Even using the store method to set the values will fail
|
136
|
+
// the unit tests.
|
137
|
+
rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), oj_num_as_value(ni));
|
138
|
+
} else {
|
139
|
+
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
static void
|
144
|
+
hash_set_value(ParseInfo pi, Val parent, VALUE value) {
|
145
|
+
if (rb_cHash != rb_obj_class(parent->val)) {
|
146
|
+
// The rb_hash_set would still work but the unit tests for the
|
147
|
+
// json gem require the less efficient []= method be called to set
|
148
|
+
// values. Even using the store method to set the values will fail
|
149
|
+
// the unit tests.
|
150
|
+
rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), value);
|
151
|
+
} else {
|
152
|
+
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
static VALUE
|
157
|
+
start_array(ParseInfo pi) {
|
158
|
+
if (Qnil != pi->options.array_class) {
|
159
|
+
return rb_class_new_instance(0, NULL, pi->options.array_class);
|
160
|
+
}
|
161
|
+
return rb_ary_new();
|
109
162
|
}
|
110
163
|
|
111
164
|
static void
|
112
165
|
array_append_num(ParseInfo pi, NumInfo ni) {
|
113
|
-
|
166
|
+
Val parent = stack_peek(&pi->stack);
|
167
|
+
|
168
|
+
if (!oj_use_array_alt && rb_cArray != rb_obj_class(parent->val)) {
|
169
|
+
// The rb_ary_push would still work but the unit tests for the json
|
170
|
+
// gem require the less efficient << method be called to push the
|
171
|
+
// values.
|
172
|
+
rb_funcall(parent->val, rb_intern("<<"), 1, oj_num_as_value(ni));
|
173
|
+
} else {
|
174
|
+
rb_ary_push(parent->val, oj_num_as_value(ni));
|
175
|
+
}
|
114
176
|
}
|
115
177
|
|
178
|
+
static void
|
179
|
+
array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
180
|
+
volatile VALUE rstr = rb_str_new(str, len);
|
181
|
+
|
182
|
+
rstr = oj_encode(rstr);
|
183
|
+
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
184
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
185
|
+
|
186
|
+
if (Qnil != clas) {
|
187
|
+
rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
rb_ary_push(stack_peek(&pi->stack)->val, rstr);
|
192
|
+
}
|
116
193
|
|
117
194
|
void
|
118
195
|
oj_set_compat_callbacks(ParseInfo pi) {
|
119
196
|
oj_set_strict_callbacks(pi);
|
197
|
+
pi->start_hash = start_hash;
|
120
198
|
pi->end_hash = end_hash;
|
121
199
|
pi->hash_set_cstr = hash_set_cstr;
|
122
|
-
pi->add_num = add_num;
|
123
200
|
pi->hash_set_num = hash_set_num;
|
201
|
+
pi->hash_set_value = hash_set_value;
|
202
|
+
pi->add_num = add_num;
|
203
|
+
pi->add_cstr = add_cstr;
|
204
|
+
pi->array_append_cstr = array_append_cstr;
|
205
|
+
pi->start_array = start_array;
|
124
206
|
pi->array_append_num = array_append_num;
|
125
207
|
}
|
126
208
|
|
@@ -128,13 +210,17 @@ VALUE
|
|
128
210
|
oj_compat_parse(int argc, VALUE *argv, VALUE self) {
|
129
211
|
struct _ParseInfo pi;
|
130
212
|
|
213
|
+
parse_info_init(&pi);
|
131
214
|
pi.options = oj_default_options;
|
132
215
|
pi.handler = Qnil;
|
133
216
|
pi.err_class = Qnil;
|
217
|
+
pi.max_depth = 0;
|
218
|
+
pi.options.allow_nan = Yes;
|
219
|
+
pi.options.nilnil = Yes;
|
134
220
|
oj_set_compat_callbacks(&pi);
|
135
221
|
|
136
222
|
if (T_STRING == rb_type(*argv)) {
|
137
|
-
return oj_pi_parse(argc, argv, &pi, 0, 0,
|
223
|
+
return oj_pi_parse(argc, argv, &pi, 0, 0, false);
|
138
224
|
} else {
|
139
225
|
return oj_pi_sparse(argc, argv, &pi, 0);
|
140
226
|
}
|
@@ -144,12 +230,14 @@ VALUE
|
|
144
230
|
oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
|
145
231
|
struct _ParseInfo pi;
|
146
232
|
|
233
|
+
parse_info_init(&pi);
|
147
234
|
pi.options = oj_default_options;
|
148
235
|
pi.handler = Qnil;
|
149
236
|
pi.err_class = Qnil;
|
150
|
-
|
151
|
-
pi.
|
152
|
-
pi.
|
237
|
+
pi.max_depth = 0;
|
238
|
+
pi.options.allow_nan = Yes;
|
239
|
+
pi.options.nilnil = Yes;
|
240
|
+
oj_set_compat_callbacks(&pi);
|
153
241
|
|
154
|
-
return oj_pi_parse(argc, argv, &pi, json, len,
|
242
|
+
return oj_pi_parse(argc, argv, &pi, json, len, false);
|
155
243
|
}
|
data/ext/oj/custom.c
ADDED
@@ -0,0 +1,1097 @@
|
|
1
|
+
/* custom.c
|
2
|
+
* Copyright (c) 2012, 2017, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include <stdio.h>
|
7
|
+
|
8
|
+
#include "code.h"
|
9
|
+
#include "dump.h"
|
10
|
+
#include "encode.h"
|
11
|
+
#include "err.h"
|
12
|
+
#include "hash.h"
|
13
|
+
#include "odd.h"
|
14
|
+
#include "oj.h"
|
15
|
+
#include "parse.h"
|
16
|
+
#include "resolve.h"
|
17
|
+
|
18
|
+
extern void oj_set_obj_ivar(Val parent, Val kval, VALUE value);
|
19
|
+
extern VALUE oj_parse_xml_time(const char *str, int len); // from object.c
|
20
|
+
|
21
|
+
static void
|
22
|
+
dump_obj_str(VALUE obj, int depth, Out out) {
|
23
|
+
struct _Attr attrs[] = {
|
24
|
+
{ "s", 1, Qnil },
|
25
|
+
{ NULL, 0, Qnil },
|
26
|
+
};
|
27
|
+
attrs->value = rb_funcall(obj, oj_to_s_id, 0);
|
28
|
+
|
29
|
+
oj_code_attrs(obj, attrs, depth, out);
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
static void
|
34
|
+
bigdecimal_dump(VALUE obj, int depth, Out out) {
|
35
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
36
|
+
const char *str = rb_string_value_ptr((VALUE*)&rstr);
|
37
|
+
int len = RSTRING_LEN(rstr);
|
38
|
+
|
39
|
+
if (0 == strcasecmp("Infinity", str)) {
|
40
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
|
41
|
+
oj_dump_raw(str, len, out);
|
42
|
+
} else if (0 == strcasecmp("-Infinity", str)) {
|
43
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
|
44
|
+
oj_dump_raw(str, len, out);
|
45
|
+
} else {
|
46
|
+
oj_dump_raw(str, len, out);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
static ID real_id = 0;
|
51
|
+
static ID imag_id = 0;
|
52
|
+
|
53
|
+
static void
|
54
|
+
complex_dump(VALUE obj, int depth, Out out) {
|
55
|
+
struct _Attr attrs[] = {
|
56
|
+
{ "real", 4, Qnil },
|
57
|
+
{ "imag", 4, Qnil },
|
58
|
+
{ NULL, 0, Qnil },
|
59
|
+
};
|
60
|
+
if (0 == real_id) {
|
61
|
+
real_id = rb_intern("real");
|
62
|
+
imag_id = rb_intern("imag");
|
63
|
+
}
|
64
|
+
attrs[0].value = rb_funcall(obj, real_id, 0);
|
65
|
+
attrs[1].value = rb_funcall(obj, imag_id, 0);
|
66
|
+
|
67
|
+
oj_code_attrs(obj, attrs, depth, out);
|
68
|
+
}
|
69
|
+
|
70
|
+
static VALUE
|
71
|
+
complex_load(VALUE clas, VALUE args) {
|
72
|
+
if (0 == real_id) {
|
73
|
+
real_id = rb_intern("real");
|
74
|
+
imag_id = rb_intern("imag");
|
75
|
+
}
|
76
|
+
return rb_complex_new(rb_hash_aref(args, rb_id2str(real_id)), rb_hash_aref(args, rb_id2str(imag_id)));
|
77
|
+
}
|
78
|
+
|
79
|
+
static void
|
80
|
+
date_dump(VALUE obj, int depth, Out out) {
|
81
|
+
struct _Attr attrs[] = {
|
82
|
+
{ "s", 1, Qnil },
|
83
|
+
{ NULL, 0, Qnil },
|
84
|
+
};
|
85
|
+
attrs->value = rb_funcall(obj, rb_intern("iso8601"), 0);
|
86
|
+
|
87
|
+
oj_code_attrs(obj, attrs, depth, out);
|
88
|
+
}
|
89
|
+
|
90
|
+
static VALUE
|
91
|
+
date_load(VALUE clas, VALUE args) {
|
92
|
+
volatile VALUE v;
|
93
|
+
|
94
|
+
if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
|
95
|
+
return rb_funcall(oj_date_class, rb_intern("parse"), 1, v);
|
96
|
+
}
|
97
|
+
return Qnil;
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE
|
101
|
+
datetime_load(VALUE clas, VALUE args) {
|
102
|
+
volatile VALUE v;
|
103
|
+
|
104
|
+
if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
|
105
|
+
return rb_funcall(oj_datetime_class, rb_intern("parse"), 1, v);
|
106
|
+
}
|
107
|
+
return Qnil;
|
108
|
+
}
|
109
|
+
|
110
|
+
static ID table_id = 0;
|
111
|
+
|
112
|
+
static void
|
113
|
+
openstruct_dump(VALUE obj, int depth, Out out) {
|
114
|
+
struct _Attr attrs[] = {
|
115
|
+
{ "table", 5, Qnil },
|
116
|
+
{ NULL, 0, Qnil },
|
117
|
+
};
|
118
|
+
if (0 == table_id) {
|
119
|
+
table_id = rb_intern("table");
|
120
|
+
}
|
121
|
+
attrs->value = rb_funcall(obj, table_id, 0);
|
122
|
+
|
123
|
+
oj_code_attrs(obj, attrs, depth, out);
|
124
|
+
}
|
125
|
+
|
126
|
+
static VALUE
|
127
|
+
openstruct_load(VALUE clas, VALUE args) {
|
128
|
+
if (0 == table_id) {
|
129
|
+
table_id = rb_intern("table");
|
130
|
+
}
|
131
|
+
return rb_funcall(clas, oj_new_id, 1, rb_hash_aref(args, rb_id2str(table_id)));
|
132
|
+
}
|
133
|
+
|
134
|
+
static void
|
135
|
+
range_dump(VALUE obj, int depth, Out out) {
|
136
|
+
struct _Attr attrs[] = {
|
137
|
+
{ "begin", 5, Qnil },
|
138
|
+
{ "end", 3, Qnil },
|
139
|
+
{ "exclude", 7, Qnil },
|
140
|
+
{ NULL, 0, Qnil },
|
141
|
+
};
|
142
|
+
attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
|
143
|
+
attrs[1].value = rb_funcall(obj, oj_end_id, 0);
|
144
|
+
attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
|
145
|
+
|
146
|
+
oj_code_attrs(obj, attrs, depth, out);
|
147
|
+
}
|
148
|
+
|
149
|
+
static VALUE
|
150
|
+
range_load(VALUE clas, VALUE args) {
|
151
|
+
VALUE nargs[3];
|
152
|
+
|
153
|
+
nargs[0] = rb_hash_aref(args, rb_id2str(oj_begin_id));
|
154
|
+
nargs[1] = rb_hash_aref(args, rb_id2str(oj_end_id));
|
155
|
+
nargs[2] = rb_hash_aref(args, rb_id2str(oj_exclude_end_id));
|
156
|
+
|
157
|
+
return rb_class_new_instance(3, nargs, rb_cRange);
|
158
|
+
}
|
159
|
+
|
160
|
+
static ID numerator_id = 0;
|
161
|
+
static ID denominator_id = 0;
|
162
|
+
|
163
|
+
static void
|
164
|
+
rational_dump(VALUE obj, int depth, Out out) {
|
165
|
+
struct _Attr attrs[] = {
|
166
|
+
{ "numerator", 9, Qnil },
|
167
|
+
{ "denominator", 11, Qnil },
|
168
|
+
{ NULL, 0, Qnil },
|
169
|
+
};
|
170
|
+
if (0 == numerator_id) {
|
171
|
+
numerator_id = rb_intern("numerator");
|
172
|
+
denominator_id = rb_intern("denominator");
|
173
|
+
}
|
174
|
+
attrs[0].value = rb_funcall(obj, numerator_id, 0);
|
175
|
+
attrs[1].value = rb_funcall(obj, denominator_id, 0);
|
176
|
+
|
177
|
+
oj_code_attrs(obj, attrs, depth, out);
|
178
|
+
}
|
179
|
+
|
180
|
+
static VALUE
|
181
|
+
rational_load(VALUE clas, VALUE args) {
|
182
|
+
if (0 == numerator_id) {
|
183
|
+
numerator_id = rb_intern("numerator");
|
184
|
+
denominator_id = rb_intern("denominator");
|
185
|
+
}
|
186
|
+
return rb_rational_new(rb_hash_aref(args, rb_id2str(numerator_id)),
|
187
|
+
rb_hash_aref(args, rb_id2str(denominator_id)));
|
188
|
+
}
|
189
|
+
|
190
|
+
static VALUE
|
191
|
+
regexp_load(VALUE clas, VALUE args) {
|
192
|
+
volatile VALUE v;
|
193
|
+
|
194
|
+
if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
|
195
|
+
return rb_funcall(rb_cRegexp, oj_new_id, 1, v);
|
196
|
+
}
|
197
|
+
return Qnil;
|
198
|
+
}
|
199
|
+
|
200
|
+
static void
|
201
|
+
time_dump(VALUE obj, int depth, Out out) {
|
202
|
+
struct _Attr attrs[] = {
|
203
|
+
{ "time", 4, Qundef, 0, Qundef },
|
204
|
+
{ NULL, 0, Qnil },
|
205
|
+
};
|
206
|
+
attrs->time = obj;
|
207
|
+
|
208
|
+
oj_code_attrs(obj, attrs, depth, out);
|
209
|
+
}
|
210
|
+
|
211
|
+
static VALUE
|
212
|
+
time_load(VALUE clas, VALUE args) {
|
213
|
+
// Value should have already been replaced in one of the hash_set_xxx
|
214
|
+
// functions.
|
215
|
+
return args;
|
216
|
+
}
|
217
|
+
|
218
|
+
static struct _Code codes[] = {
|
219
|
+
{ "BigDecimal", Qnil, bigdecimal_dump, NULL, true },
|
220
|
+
{ "Complex", Qnil, complex_dump, complex_load, true },
|
221
|
+
{ "Date", Qnil, date_dump, date_load, true },
|
222
|
+
{ "DateTime", Qnil, date_dump, datetime_load, true },
|
223
|
+
{ "OpenStruct", Qnil, openstruct_dump, openstruct_load, true },
|
224
|
+
{ "Range", Qnil, range_dump, range_load, true },
|
225
|
+
{ "Rational", Qnil, rational_dump, rational_load, true },
|
226
|
+
{ "Regexp", Qnil, dump_obj_str, regexp_load, true },
|
227
|
+
{ "Time", Qnil, time_dump, time_load, true },
|
228
|
+
{ NULL, Qundef, NULL, NULL, false },
|
229
|
+
};
|
230
|
+
|
231
|
+
static int
|
232
|
+
hash_cb(VALUE key, VALUE value, Out out) {
|
233
|
+
int depth = out->depth;
|
234
|
+
|
235
|
+
if (out->omit_nil && Qnil == value) {
|
236
|
+
return ST_CONTINUE;
|
237
|
+
}
|
238
|
+
if (!out->opts->dump_opts.use) {
|
239
|
+
assure_size(out, depth * out->indent + 1);
|
240
|
+
fill_indent(out, depth);
|
241
|
+
} else {
|
242
|
+
assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
|
243
|
+
if (0 < out->opts->dump_opts.hash_size) {
|
244
|
+
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
245
|
+
out->cur += out->opts->dump_opts.hash_size;
|
246
|
+
}
|
247
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
248
|
+
int i;
|
249
|
+
|
250
|
+
for (i = depth; 0 < i; i--) {
|
251
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
252
|
+
out->cur += out->opts->dump_opts.indent_size;
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
switch (rb_type(key)) {
|
257
|
+
case T_STRING:
|
258
|
+
oj_dump_str(key, 0, out, false);
|
259
|
+
break;
|
260
|
+
case T_SYMBOL:
|
261
|
+
oj_dump_sym(key, 0, out, false);
|
262
|
+
break;
|
263
|
+
default:
|
264
|
+
oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
|
265
|
+
break;
|
266
|
+
}
|
267
|
+
if (!out->opts->dump_opts.use) {
|
268
|
+
*out->cur++ = ':';
|
269
|
+
} else {
|
270
|
+
assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
|
271
|
+
if (0 < out->opts->dump_opts.before_size) {
|
272
|
+
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
273
|
+
out->cur += out->opts->dump_opts.before_size;
|
274
|
+
}
|
275
|
+
*out->cur++ = ':';
|
276
|
+
if (0 < out->opts->dump_opts.after_size) {
|
277
|
+
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
278
|
+
out->cur += out->opts->dump_opts.after_size;
|
279
|
+
}
|
280
|
+
}
|
281
|
+
oj_dump_custom_val(value, depth, out, true);
|
282
|
+
out->depth = depth;
|
283
|
+
*out->cur++ = ',';
|
284
|
+
|
285
|
+
return ST_CONTINUE;
|
286
|
+
}
|
287
|
+
|
288
|
+
static void
|
289
|
+
dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
|
290
|
+
int cnt;
|
291
|
+
long id = oj_check_circular(obj, out);
|
292
|
+
|
293
|
+
if (0 > id) {
|
294
|
+
oj_dump_nil(Qnil, depth, out, false);
|
295
|
+
return;
|
296
|
+
}
|
297
|
+
cnt = (int)RHASH_SIZE(obj);
|
298
|
+
assure_size(out, 2);
|
299
|
+
if (0 == cnt) {
|
300
|
+
*out->cur++ = '{';
|
301
|
+
*out->cur++ = '}';
|
302
|
+
} else {
|
303
|
+
*out->cur++ = '{';
|
304
|
+
out->depth = depth + 1;
|
305
|
+
rb_hash_foreach(obj, hash_cb, (VALUE)out);
|
306
|
+
if (',' == *(out->cur - 1)) {
|
307
|
+
out->cur--; // backup to overwrite last comma
|
308
|
+
}
|
309
|
+
if (!out->opts->dump_opts.use) {
|
310
|
+
assure_size(out, depth * out->indent + 2);
|
311
|
+
fill_indent(out, depth);
|
312
|
+
} else {
|
313
|
+
assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
|
314
|
+
if (0 < out->opts->dump_opts.hash_size) {
|
315
|
+
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
316
|
+
out->cur += out->opts->dump_opts.hash_size;
|
317
|
+
}
|
318
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
319
|
+
int i;
|
320
|
+
|
321
|
+
for (i = depth; 0 < i; i--) {
|
322
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
323
|
+
out->cur += out->opts->dump_opts.indent_size;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
*out->cur++ = '}';
|
328
|
+
}
|
329
|
+
*out->cur = '\0';
|
330
|
+
}
|
331
|
+
|
332
|
+
static void
|
333
|
+
dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
|
334
|
+
ID *idp;
|
335
|
+
AttrGetFunc *fp;
|
336
|
+
volatile VALUE v;
|
337
|
+
const char *name;
|
338
|
+
size_t size;
|
339
|
+
int d2 = depth + 1;
|
340
|
+
|
341
|
+
assure_size(out, 2);
|
342
|
+
*out->cur++ = '{';
|
343
|
+
if (NULL != out->opts->create_id) {
|
344
|
+
const char *classname = rb_class2name(clas);
|
345
|
+
int clen = (int)strlen(classname);
|
346
|
+
size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
347
|
+
|
348
|
+
size = d2 * out->indent + 10 + clen + out->opts->create_id_len + sep_len;
|
349
|
+
assure_size(out, size);
|
350
|
+
fill_indent(out, d2);
|
351
|
+
*out->cur++ = '"';
|
352
|
+
memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
|
353
|
+
out->cur += out->opts->create_id_len;
|
354
|
+
*out->cur++ = '"';
|
355
|
+
if (0 < out->opts->dump_opts.before_size) {
|
356
|
+
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
357
|
+
out->cur += out->opts->dump_opts.before_size;
|
358
|
+
}
|
359
|
+
*out->cur++ = ':';
|
360
|
+
if (0 < out->opts->dump_opts.after_size) {
|
361
|
+
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
362
|
+
out->cur += out->opts->dump_opts.after_size;
|
363
|
+
}
|
364
|
+
*out->cur++ = '"';
|
365
|
+
memcpy(out->cur, classname, clen);
|
366
|
+
out->cur += clen;
|
367
|
+
*out->cur++ = '"';
|
368
|
+
*out->cur++ = ',';
|
369
|
+
}
|
370
|
+
if (odd->raw) {
|
371
|
+
v = rb_funcall(obj, *odd->attrs, 0);
|
372
|
+
if (Qundef == v || T_STRING != rb_type(v)) {
|
373
|
+
rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
|
374
|
+
} else {
|
375
|
+
const char *s = rb_string_value_ptr((VALUE*)&v);
|
376
|
+
int len = RSTRING_LEN(v);
|
377
|
+
const char *name = rb_id2name(*odd->attrs);
|
378
|
+
size_t nlen = strlen(name);
|
379
|
+
|
380
|
+
size = len + d2 * out->indent + nlen + 10;
|
381
|
+
assure_size(out, size);
|
382
|
+
fill_indent(out, d2);
|
383
|
+
*out->cur++ = '"';
|
384
|
+
memcpy(out->cur, name, nlen);
|
385
|
+
out->cur += nlen;
|
386
|
+
*out->cur++ = '"';
|
387
|
+
*out->cur++ = ':';
|
388
|
+
memcpy(out->cur, s, len);
|
389
|
+
out->cur += len;
|
390
|
+
*out->cur = '\0';
|
391
|
+
}
|
392
|
+
} else {
|
393
|
+
size = d2 * out->indent + 1;
|
394
|
+
for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
|
395
|
+
size_t nlen;
|
396
|
+
|
397
|
+
assure_size(out, size);
|
398
|
+
name = rb_id2name(*idp);
|
399
|
+
nlen = strlen(name);
|
400
|
+
if (0 != *fp) {
|
401
|
+
v = (*fp)(obj);
|
402
|
+
} else if (0 == strchr(name, '.')) {
|
403
|
+
v = rb_funcall(obj, *idp, 0);
|
404
|
+
} else {
|
405
|
+
char nbuf[256];
|
406
|
+
char *n2 = nbuf;
|
407
|
+
char *n;
|
408
|
+
char *end;
|
409
|
+
ID i;
|
410
|
+
|
411
|
+
if (sizeof(nbuf) <= nlen) {
|
412
|
+
n2 = strdup(name);
|
413
|
+
} else {
|
414
|
+
strcpy(n2, name);
|
415
|
+
}
|
416
|
+
n = n2;
|
417
|
+
v = obj;
|
418
|
+
while (0 != (end = strchr(n, '.'))) {
|
419
|
+
*end = '\0';
|
420
|
+
i = rb_intern(n);
|
421
|
+
v = rb_funcall(v, i, 0);
|
422
|
+
n = end + 1;
|
423
|
+
}
|
424
|
+
i = rb_intern(n);
|
425
|
+
v = rb_funcall(v, i, 0);
|
426
|
+
if (nbuf != n2) {
|
427
|
+
free(n2);
|
428
|
+
}
|
429
|
+
}
|
430
|
+
fill_indent(out, d2);
|
431
|
+
oj_dump_cstr(name, nlen, 0, 0, out);
|
432
|
+
*out->cur++ = ':';
|
433
|
+
oj_dump_custom_val(v, d2, out, true);
|
434
|
+
assure_size(out, 2);
|
435
|
+
*out->cur++ = ',';
|
436
|
+
}
|
437
|
+
out->cur--;
|
438
|
+
}
|
439
|
+
*out->cur++ = '}';
|
440
|
+
*out->cur = '\0';
|
441
|
+
}
|
442
|
+
|
443
|
+
// Return class if still needs dumping.
|
444
|
+
static VALUE
|
445
|
+
dump_common(VALUE obj, int depth, Out out) {
|
446
|
+
if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
447
|
+
volatile VALUE rs;
|
448
|
+
const char *s;
|
449
|
+
int len;
|
450
|
+
|
451
|
+
rs = rb_funcall(obj, oj_to_json_id, 0);
|
452
|
+
s = rb_string_value_ptr((VALUE*)&rs);
|
453
|
+
len = (int)RSTRING_LEN(rs);
|
454
|
+
|
455
|
+
assure_size(out, len + 1);
|
456
|
+
memcpy(out->cur, s, len);
|
457
|
+
out->cur += len;
|
458
|
+
*out->cur = '\0';
|
459
|
+
} else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
460
|
+
volatile VALUE aj;
|
461
|
+
int arity;
|
462
|
+
|
463
|
+
#if HAS_METHOD_ARITY
|
464
|
+
arity = rb_obj_method_arity(obj, oj_as_json_id);
|
465
|
+
#else
|
466
|
+
arity = out->argc;
|
467
|
+
#endif
|
468
|
+
// Some classes elect to not take an options argument so check the arity
|
469
|
+
// of as_json.
|
470
|
+
switch (arity) {
|
471
|
+
case 0:
|
472
|
+
aj = rb_funcall(obj, oj_as_json_id, 0);
|
473
|
+
break;
|
474
|
+
case 1:
|
475
|
+
if (1 <= out->argc) {
|
476
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, (VALUE*)out->argv);
|
477
|
+
} else {
|
478
|
+
aj = rb_funcall(obj, oj_as_json_id, 1, Qnil);
|
479
|
+
}
|
480
|
+
break;
|
481
|
+
default:
|
482
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
483
|
+
break;
|
484
|
+
}
|
485
|
+
// Catch the obvious brain damaged recursive dumping.
|
486
|
+
if (aj == obj) {
|
487
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
488
|
+
|
489
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), false, false, out);
|
490
|
+
} else {
|
491
|
+
oj_dump_custom_val(aj, depth, out, true);
|
492
|
+
}
|
493
|
+
} else if (Yes == out->opts->to_hash && rb_respond_to(obj, oj_to_hash_id)) {
|
494
|
+
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
495
|
+
|
496
|
+
if (T_HASH != rb_type(h)) {
|
497
|
+
// It seems that ActiveRecord implemented to_hash so that it returns
|
498
|
+
// an Array and not a Hash. To get around that any value returned
|
499
|
+
// will be dumped.
|
500
|
+
|
501
|
+
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
502
|
+
oj_dump_custom_val(h, depth, out, false);
|
503
|
+
} else {
|
504
|
+
dump_hash(h, depth, out, true);
|
505
|
+
}
|
506
|
+
} else if (!oj_code_dump(codes, obj, depth, out)) {
|
507
|
+
VALUE clas = rb_obj_class(obj);
|
508
|
+
Odd odd = oj_get_odd(clas);
|
509
|
+
|
510
|
+
if (NULL == odd) {
|
511
|
+
return clas;
|
512
|
+
}
|
513
|
+
dump_odd(obj, odd, clas, depth + 1, out);
|
514
|
+
}
|
515
|
+
return Qnil;
|
516
|
+
}
|
517
|
+
|
518
|
+
static int
|
519
|
+
dump_attr_cb(ID key, VALUE value, Out out) {
|
520
|
+
int depth = out->depth;
|
521
|
+
size_t size;
|
522
|
+
const char *attr;
|
523
|
+
|
524
|
+
if (out->omit_nil && Qnil == value) {
|
525
|
+
return ST_CONTINUE;
|
526
|
+
}
|
527
|
+
size = depth * out->indent + 1;
|
528
|
+
attr = rb_id2name(key);
|
529
|
+
|
530
|
+
#if HAS_EXCEPTION_MAGIC
|
531
|
+
if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
|
532
|
+
return ST_CONTINUE;
|
533
|
+
}
|
534
|
+
#endif
|
535
|
+
assure_size(out, size);
|
536
|
+
fill_indent(out, depth);
|
537
|
+
if ('@' == *attr) {
|
538
|
+
attr++;
|
539
|
+
oj_dump_cstr(attr, strlen(attr), 0, 0, out);
|
540
|
+
} else {
|
541
|
+
char buf[32];
|
542
|
+
|
543
|
+
*buf = '~';
|
544
|
+
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
545
|
+
buf[sizeof(buf) - 1] = '\0';
|
546
|
+
oj_dump_cstr(buf, strlen(buf), 0, 0, out);
|
547
|
+
}
|
548
|
+
*out->cur++ = ':';
|
549
|
+
oj_dump_custom_val(value, depth, out, true);
|
550
|
+
out->depth = depth;
|
551
|
+
*out->cur++ = ',';
|
552
|
+
|
553
|
+
return ST_CONTINUE;
|
554
|
+
}
|
555
|
+
|
556
|
+
static void
|
557
|
+
dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
558
|
+
size_t size = 0;
|
559
|
+
int d2 = depth + 1;
|
560
|
+
int cnt;
|
561
|
+
|
562
|
+
assure_size(out, 2);
|
563
|
+
*out->cur++ = '{';
|
564
|
+
if (Qundef != clas) {
|
565
|
+
size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
566
|
+
const char *classname = rb_obj_classname(obj);
|
567
|
+
size_t len = strlen(classname);
|
568
|
+
|
569
|
+
size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
|
570
|
+
assure_size(out, size);
|
571
|
+
fill_indent(out, d2);
|
572
|
+
*out->cur++ = '"';
|
573
|
+
memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
|
574
|
+
out->cur += out->opts->create_id_len;
|
575
|
+
*out->cur++ = '"';
|
576
|
+
if (0 < out->opts->dump_opts.before_size) {
|
577
|
+
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
578
|
+
out->cur += out->opts->dump_opts.before_size;
|
579
|
+
}
|
580
|
+
*out->cur++ = ':';
|
581
|
+
if (0 < out->opts->dump_opts.after_size) {
|
582
|
+
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
583
|
+
out->cur += out->opts->dump_opts.after_size;
|
584
|
+
}
|
585
|
+
*out->cur++ = '"';
|
586
|
+
memcpy(out->cur, classname, len);
|
587
|
+
out->cur += len;
|
588
|
+
*out->cur++ = '"';
|
589
|
+
}
|
590
|
+
cnt = (int)rb_ivar_count(obj);
|
591
|
+
if (Qundef != clas && 0 < cnt) {
|
592
|
+
*out->cur++ = ',';
|
593
|
+
}
|
594
|
+
if (0 == cnt && Qundef == clas) {
|
595
|
+
// Might be something special like an Enumerable.
|
596
|
+
if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
|
597
|
+
out->cur--;
|
598
|
+
oj_dump_custom_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out, false);
|
599
|
+
return;
|
600
|
+
}
|
601
|
+
}
|
602
|
+
out->depth = depth + 1;
|
603
|
+
rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
|
604
|
+
if (',' == *(out->cur - 1)) {
|
605
|
+
out->cur--; // backup to overwrite last comma
|
606
|
+
}
|
607
|
+
#if HAS_EXCEPTION_MAGIC
|
608
|
+
if (rb_obj_is_kind_of(obj, rb_eException)) {
|
609
|
+
volatile VALUE rv;
|
610
|
+
|
611
|
+
if (',' != *(out->cur - 1)) {
|
612
|
+
*out->cur++ = ',';
|
613
|
+
}
|
614
|
+
// message
|
615
|
+
assure_size(out, 2);
|
616
|
+
fill_indent(out, d2);
|
617
|
+
oj_dump_cstr("~mesg", 5, 0, 0, out);
|
618
|
+
*out->cur++ = ':';
|
619
|
+
rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
|
620
|
+
oj_dump_custom_val(rv, d2, out, true);
|
621
|
+
assure_size(out, size + 2);
|
622
|
+
*out->cur++ = ',';
|
623
|
+
// backtrace
|
624
|
+
fill_indent(out, d2);
|
625
|
+
oj_dump_cstr("~bt", 3, 0, 0, out);
|
626
|
+
*out->cur++ = ':';
|
627
|
+
rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
|
628
|
+
oj_dump_custom_val(rv, d2, out, true);
|
629
|
+
assure_size(out, 2);
|
630
|
+
}
|
631
|
+
#endif
|
632
|
+
out->depth = depth;
|
633
|
+
|
634
|
+
fill_indent(out, depth);
|
635
|
+
*out->cur++ = '}';
|
636
|
+
*out->cur = '\0';
|
637
|
+
}
|
638
|
+
|
639
|
+
static void
|
640
|
+
dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
641
|
+
long id = oj_check_circular(obj, out);
|
642
|
+
VALUE clas;
|
643
|
+
|
644
|
+
if (0 > id) {
|
645
|
+
oj_dump_nil(Qnil, depth, out, false);
|
646
|
+
} else if (Qnil != (clas = dump_common(obj, depth, out))) {
|
647
|
+
dump_obj_attrs(obj, clas, 0, depth, out);
|
648
|
+
}
|
649
|
+
*out->cur = '\0';
|
650
|
+
}
|
651
|
+
|
652
|
+
static void
|
653
|
+
dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
654
|
+
size_t size;
|
655
|
+
int i, cnt;
|
656
|
+
int d2 = depth + 1;
|
657
|
+
long id = oj_check_circular(a, out);
|
658
|
+
|
659
|
+
if (0 > id) {
|
660
|
+
oj_dump_nil(Qnil, depth, out, false);
|
661
|
+
return;
|
662
|
+
}
|
663
|
+
cnt = (int)RARRAY_LEN(a);
|
664
|
+
*out->cur++ = '[';
|
665
|
+
assure_size(out, 2);
|
666
|
+
if (0 == cnt) {
|
667
|
+
*out->cur++ = ']';
|
668
|
+
} else {
|
669
|
+
if (out->opts->dump_opts.use) {
|
670
|
+
size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
|
671
|
+
} else {
|
672
|
+
size = d2 * out->indent + 2;
|
673
|
+
}
|
674
|
+
cnt--;
|
675
|
+
for (i = 0; i <= cnt; i++) {
|
676
|
+
assure_size(out, size);
|
677
|
+
if (out->opts->dump_opts.use) {
|
678
|
+
if (0 < out->opts->dump_opts.array_size) {
|
679
|
+
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
680
|
+
out->cur += out->opts->dump_opts.array_size;
|
681
|
+
}
|
682
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
683
|
+
int i;
|
684
|
+
for (i = d2; 0 < i; i--) {
|
685
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
686
|
+
out->cur += out->opts->dump_opts.indent_size;
|
687
|
+
}
|
688
|
+
}
|
689
|
+
} else {
|
690
|
+
fill_indent(out, d2);
|
691
|
+
}
|
692
|
+
oj_dump_custom_val(rb_ary_entry(a, i), d2, out, true);
|
693
|
+
if (i < cnt) {
|
694
|
+
*out->cur++ = ',';
|
695
|
+
}
|
696
|
+
}
|
697
|
+
size = depth * out->indent + 1;
|
698
|
+
assure_size(out, size);
|
699
|
+
if (out->opts->dump_opts.use) {
|
700
|
+
if (0 < out->opts->dump_opts.array_size) {
|
701
|
+
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
702
|
+
out->cur += out->opts->dump_opts.array_size;
|
703
|
+
}
|
704
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
705
|
+
int i;
|
706
|
+
|
707
|
+
for (i = depth; 0 < i; i--) {
|
708
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
709
|
+
out->cur += out->opts->dump_opts.indent_size;
|
710
|
+
}
|
711
|
+
}
|
712
|
+
} else {
|
713
|
+
fill_indent(out, depth);
|
714
|
+
}
|
715
|
+
*out->cur++ = ']';
|
716
|
+
}
|
717
|
+
*out->cur = '\0';
|
718
|
+
}
|
719
|
+
|
720
|
+
static void
|
721
|
+
dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
|
722
|
+
long id = oj_check_circular(obj, out);
|
723
|
+
VALUE clas;
|
724
|
+
|
725
|
+
if (0 > id) {
|
726
|
+
oj_dump_nil(Qnil, depth, out, false);
|
727
|
+
} else if (Qnil != (clas = dump_common(obj, depth, out))) {
|
728
|
+
VALUE ma = Qnil;
|
729
|
+
VALUE v;
|
730
|
+
char num_id[32];
|
731
|
+
int i;
|
732
|
+
int d2 = depth + 1;
|
733
|
+
int d3 = d2 + 1;
|
734
|
+
size_t size = d2 * out->indent + d3 * out->indent + 3;
|
735
|
+
const char *name;
|
736
|
+
int cnt;
|
737
|
+
size_t len;
|
738
|
+
|
739
|
+
assure_size(out, size);
|
740
|
+
if (clas == rb_cRange) {
|
741
|
+
*out->cur++ = '"';
|
742
|
+
oj_dump_custom_val(rb_funcall(obj, oj_begin_id, 0), d3, out, false);
|
743
|
+
assure_size(out, 3);
|
744
|
+
*out->cur++ = '.';
|
745
|
+
*out->cur++ = '.';
|
746
|
+
if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
|
747
|
+
*out->cur++ = '.';
|
748
|
+
}
|
749
|
+
oj_dump_custom_val(rb_funcall(obj, oj_end_id, 0), d3, out, false);
|
750
|
+
*out->cur++ = '"';
|
751
|
+
|
752
|
+
return;
|
753
|
+
}
|
754
|
+
*out->cur++ = '{';
|
755
|
+
fill_indent(out, d2);
|
756
|
+
size = d3 * out->indent + 2;
|
757
|
+
#if HAS_STRUCT_MEMBERS
|
758
|
+
ma = rb_struct_s_members(clas);
|
759
|
+
#endif
|
760
|
+
|
761
|
+
#ifdef RSTRUCT_LEN
|
762
|
+
#if UNIFY_FIXNUM_AND_BIGNUM
|
763
|
+
cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
|
764
|
+
#else // UNIFY_FIXNUM_AND_INTEGER
|
765
|
+
cnt = (int)RSTRUCT_LEN(obj);
|
766
|
+
#endif // UNIFY_FIXNUM_AND_INTEGER
|
767
|
+
#else
|
768
|
+
// This is a bit risky as a struct in C ruby is not the same as a Struct
|
769
|
+
// class in interpreted Ruby so length() may not be defined.
|
770
|
+
cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
|
771
|
+
#endif
|
772
|
+
for (i = 0; i < cnt; i++) {
|
773
|
+
#ifdef RSTRUCT_LEN
|
774
|
+
v = RSTRUCT_GET(obj, i);
|
775
|
+
#else
|
776
|
+
v = rb_struct_aref(obj, INT2FIX(i));
|
777
|
+
#endif
|
778
|
+
if (ma != Qnil) {
|
779
|
+
name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
|
780
|
+
len = strlen(name);
|
781
|
+
} else {
|
782
|
+
len = snprintf(num_id, sizeof(num_id), "%d", i);
|
783
|
+
name = num_id;
|
784
|
+
}
|
785
|
+
assure_size(out, size + len + 3);
|
786
|
+
fill_indent(out, d3);
|
787
|
+
*out->cur++ = '"';
|
788
|
+
memcpy(out->cur, name, len);
|
789
|
+
out->cur += len;
|
790
|
+
*out->cur++ = '"';
|
791
|
+
*out->cur++ = ':';
|
792
|
+
oj_dump_custom_val(v, d3, out, true);
|
793
|
+
*out->cur++ = ',';
|
794
|
+
}
|
795
|
+
out->cur--;
|
796
|
+
*out->cur++ = '}';
|
797
|
+
*out->cur = '\0';
|
798
|
+
}
|
799
|
+
}
|
800
|
+
|
801
|
+
static void
|
802
|
+
dump_data(VALUE obj, int depth, Out out, bool as_ok) {
|
803
|
+
long id = oj_check_circular(obj, out);
|
804
|
+
VALUE clas;
|
805
|
+
|
806
|
+
if (0 > id) {
|
807
|
+
oj_dump_nil(Qnil, depth, out, false);
|
808
|
+
} else if (Qnil != (clas = dump_common(obj, depth, out))) {
|
809
|
+
dump_obj_attrs(obj, clas, id, depth, out);
|
810
|
+
}
|
811
|
+
}
|
812
|
+
|
813
|
+
static void
|
814
|
+
dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
|
815
|
+
dump_obj_str(obj, depth, out);
|
816
|
+
}
|
817
|
+
|
818
|
+
static void
|
819
|
+
dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
|
820
|
+
complex_dump(obj, depth, out);
|
821
|
+
}
|
822
|
+
|
823
|
+
static void
|
824
|
+
dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
|
825
|
+
rational_dump(obj, depth, out);
|
826
|
+
}
|
827
|
+
|
828
|
+
static DumpFunc custom_funcs[] = {
|
829
|
+
NULL, // RUBY_T_NONE = 0x00,
|
830
|
+
dump_obj, // RUBY_T_OBJECT = 0x01,
|
831
|
+
oj_dump_class, // RUBY_T_CLASS = 0x02,
|
832
|
+
oj_dump_class, // RUBY_T_MODULE = 0x03,
|
833
|
+
oj_dump_float, // RUBY_T_FLOAT = 0x04,
|
834
|
+
oj_dump_str, // RUBY_T_STRING = 0x05,
|
835
|
+
dump_regexp, // RUBY_T_REGEXP = 0x06,
|
836
|
+
dump_array, // RUBY_T_ARRAY = 0x07,
|
837
|
+
dump_hash, // RUBY_T_HASH = 0x08,
|
838
|
+
dump_struct, // RUBY_T_STRUCT = 0x09,
|
839
|
+
oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
|
840
|
+
NULL, // RUBY_T_FILE = 0x0b,
|
841
|
+
dump_data, // RUBY_T_DATA = 0x0c,
|
842
|
+
NULL, // RUBY_T_MATCH = 0x0d,
|
843
|
+
dump_complex, // RUBY_T_COMPLEX = 0x0e,
|
844
|
+
dump_rational, // RUBY_T_RATIONAL = 0x0f,
|
845
|
+
NULL, // 0x10
|
846
|
+
oj_dump_nil, // RUBY_T_NIL = 0x11,
|
847
|
+
oj_dump_true, // RUBY_T_TRUE = 0x12,
|
848
|
+
oj_dump_false, // RUBY_T_FALSE = 0x13,
|
849
|
+
oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
|
850
|
+
oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
|
851
|
+
};
|
852
|
+
|
853
|
+
void
|
854
|
+
oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
|
855
|
+
int type = rb_type(obj);
|
856
|
+
|
857
|
+
if (MAX_DEPTH < depth) {
|
858
|
+
rb_raise(rb_eNoMemError, "Too deeply nested.\n");
|
859
|
+
}
|
860
|
+
if (0 < type && type <= RUBY_T_FIXNUM) {
|
861
|
+
DumpFunc f = custom_funcs[type];
|
862
|
+
|
863
|
+
if (NULL != f) {
|
864
|
+
f(obj, depth, out, true);
|
865
|
+
return;
|
866
|
+
}
|
867
|
+
}
|
868
|
+
oj_dump_nil(Qnil, depth, out, false);
|
869
|
+
}
|
870
|
+
|
871
|
+
///// load functions /////
|
872
|
+
|
873
|
+
static void
|
874
|
+
hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
875
|
+
const char *key = kval->key;
|
876
|
+
int klen = kval->klen;
|
877
|
+
Val parent = stack_peek(&pi->stack);
|
878
|
+
volatile VALUE rkey = kval->key_val;
|
879
|
+
|
880
|
+
if (Qundef == rkey &&
|
881
|
+
Yes == pi->options.create_ok &&
|
882
|
+
NULL != pi->options.create_id &&
|
883
|
+
*pi->options.create_id == *key &&
|
884
|
+
(int)pi->options.create_id_len == klen &&
|
885
|
+
0 == strncmp(pi->options.create_id, key, klen)) {
|
886
|
+
|
887
|
+
parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
|
888
|
+
if (2 == klen && '^' == *key && 'o' == key[1]) {
|
889
|
+
if (Qundef != parent->clas) {
|
890
|
+
if (!oj_code_has(codes, parent->clas, false)) {
|
891
|
+
parent->val = rb_obj_alloc(parent->clas);
|
892
|
+
}
|
893
|
+
}
|
894
|
+
}
|
895
|
+
} else {
|
896
|
+
volatile VALUE rstr = rb_str_new(str, len);
|
897
|
+
|
898
|
+
if (Qundef == rkey) {
|
899
|
+
rkey = rb_str_new(key, klen);
|
900
|
+
rstr = oj_encode(rstr);
|
901
|
+
rkey = oj_encode(rkey);
|
902
|
+
if (Yes == pi->options.sym_key) {
|
903
|
+
rkey = rb_str_intern(rkey);
|
904
|
+
}
|
905
|
+
}
|
906
|
+
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
907
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
908
|
+
|
909
|
+
if (Qnil != clas) {
|
910
|
+
rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
911
|
+
}
|
912
|
+
}
|
913
|
+
switch (rb_type(parent->val)) {
|
914
|
+
case T_OBJECT:
|
915
|
+
oj_set_obj_ivar(parent, kval, rstr);
|
916
|
+
break;
|
917
|
+
case T_HASH:
|
918
|
+
if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
|
919
|
+
if (Qnil == (parent->val = oj_parse_xml_time(str, len))) {
|
920
|
+
parent->val = rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_str_new(str, len));
|
921
|
+
}
|
922
|
+
} else {
|
923
|
+
rb_hash_aset(parent->val, rkey, rstr);
|
924
|
+
}
|
925
|
+
break;
|
926
|
+
default:
|
927
|
+
break;
|
928
|
+
}
|
929
|
+
}
|
930
|
+
}
|
931
|
+
|
932
|
+
static void
|
933
|
+
end_hash(struct _ParseInfo *pi) {
|
934
|
+
Val parent = stack_peek(&pi->stack);
|
935
|
+
|
936
|
+
if (Qundef != parent->clas && parent->clas != rb_obj_class(parent->val)) {
|
937
|
+
volatile VALUE obj = oj_code_load(codes, parent->clas, parent->val);
|
938
|
+
|
939
|
+
if (Qnil != obj) {
|
940
|
+
parent->val = obj;
|
941
|
+
} else {
|
942
|
+
parent->val = rb_funcall(parent->clas, oj_json_create_id, 1, parent->val);
|
943
|
+
}
|
944
|
+
parent->clas = Qundef;
|
945
|
+
}
|
946
|
+
}
|
947
|
+
|
948
|
+
static VALUE
|
949
|
+
calc_hash_key(ParseInfo pi, Val parent) {
|
950
|
+
volatile VALUE rkey = parent->key_val;
|
951
|
+
|
952
|
+
if (Qundef == rkey) {
|
953
|
+
rkey = rb_str_new(parent->key, parent->klen);
|
954
|
+
}
|
955
|
+
rkey = oj_encode(rkey);
|
956
|
+
if (Yes == pi->options.sym_key) {
|
957
|
+
rkey = rb_str_intern(rkey);
|
958
|
+
}
|
959
|
+
return rkey;
|
960
|
+
}
|
961
|
+
|
962
|
+
static void
|
963
|
+
hash_set_num(struct _ParseInfo *pi, Val kval, NumInfo ni) {
|
964
|
+
Val parent = stack_peek(&pi->stack);
|
965
|
+
|
966
|
+
switch (rb_type(parent->val)) {
|
967
|
+
case T_OBJECT:
|
968
|
+
oj_set_obj_ivar(parent, kval, oj_num_as_value(ni));
|
969
|
+
break;
|
970
|
+
case T_HASH:
|
971
|
+
if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
|
972
|
+
int64_t nsec = ni->num * 1000000000LL / ni->div;
|
973
|
+
|
974
|
+
if (ni->neg) {
|
975
|
+
ni->i = -ni->i;
|
976
|
+
if (0 < nsec) {
|
977
|
+
ni->i--;
|
978
|
+
nsec = 1000000000LL - nsec;
|
979
|
+
}
|
980
|
+
}
|
981
|
+
if (86400 == ni->exp) { // UTC time
|
982
|
+
parent->val = rb_time_nano_new(ni->i, (long)nsec);
|
983
|
+
// Since the ruby C routines alway create local time, the
|
984
|
+
// offset and then a convertion to UTC keeps makes the time
|
985
|
+
// match the expected value.
|
986
|
+
parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
|
987
|
+
} else if (ni->hasExp) {
|
988
|
+
time_t t = (time_t)(ni->i + ni->exp);
|
989
|
+
struct tm *st = gmtime(&t);
|
990
|
+
VALUE args[8];
|
991
|
+
|
992
|
+
args[0] = LONG2NUM(1900 + st->tm_year);
|
993
|
+
args[1] = LONG2NUM(1 + st->tm_mon);
|
994
|
+
args[2] = LONG2NUM(st->tm_mday);
|
995
|
+
args[3] = LONG2NUM(st->tm_hour);
|
996
|
+
args[4] = LONG2NUM(st->tm_min);
|
997
|
+
args[5] = rb_float_new((double)st->tm_sec + ((double)nsec + 0.5) / 1000000000.0);
|
998
|
+
args[6] = LONG2NUM(ni->exp);
|
999
|
+
parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
|
1000
|
+
} else {
|
1001
|
+
parent->val = rb_time_nano_new(ni->i, (long)nsec);
|
1002
|
+
}
|
1003
|
+
} else {
|
1004
|
+
rb_hash_aset(parent->val, calc_hash_key(pi, kval), oj_num_as_value(ni));
|
1005
|
+
}
|
1006
|
+
break;
|
1007
|
+
default:
|
1008
|
+
break;
|
1009
|
+
}
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
static void
|
1013
|
+
hash_set_value(ParseInfo pi, Val kval, VALUE value) {
|
1014
|
+
Val parent = stack_peek(&pi->stack);
|
1015
|
+
|
1016
|
+
switch (rb_type(parent->val)) {
|
1017
|
+
case T_OBJECT:
|
1018
|
+
oj_set_obj_ivar(parent, kval, value);
|
1019
|
+
break;
|
1020
|
+
case T_HASH:
|
1021
|
+
rb_hash_aset(parent->val, calc_hash_key(pi, kval), value);
|
1022
|
+
break;
|
1023
|
+
default:
|
1024
|
+
break;
|
1025
|
+
}
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
static void
|
1029
|
+
array_append_num(ParseInfo pi, NumInfo ni) {
|
1030
|
+
Val parent = stack_peek(&pi->stack);
|
1031
|
+
|
1032
|
+
rb_ary_push(parent->val, oj_num_as_value(ni));
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
static void
|
1036
|
+
array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
1037
|
+
volatile VALUE rstr = rb_str_new(str, len);
|
1038
|
+
|
1039
|
+
rstr = oj_encode(rstr);
|
1040
|
+
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
1041
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
1042
|
+
|
1043
|
+
if (Qnil != clas) {
|
1044
|
+
rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
|
1045
|
+
return;
|
1046
|
+
}
|
1047
|
+
}
|
1048
|
+
rb_ary_push(stack_peek(&pi->stack)->val, rstr);
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
void
|
1052
|
+
oj_set_custom_callbacks(ParseInfo pi) {
|
1053
|
+
oj_set_compat_callbacks(pi);
|
1054
|
+
pi->hash_set_cstr = hash_set_cstr;
|
1055
|
+
pi->end_hash = end_hash;
|
1056
|
+
pi->hash_set_num = hash_set_num;
|
1057
|
+
pi->hash_set_value = hash_set_value;
|
1058
|
+
pi->array_append_cstr = array_append_cstr;
|
1059
|
+
pi->array_append_num = array_append_num;
|
1060
|
+
}
|
1061
|
+
|
1062
|
+
VALUE
|
1063
|
+
oj_custom_parse(int argc, VALUE *argv, VALUE self) {
|
1064
|
+
struct _ParseInfo pi;
|
1065
|
+
|
1066
|
+
parse_info_init(&pi);
|
1067
|
+
pi.options = oj_default_options;
|
1068
|
+
pi.handler = Qnil;
|
1069
|
+
pi.err_class = Qnil;
|
1070
|
+
pi.max_depth = 0;
|
1071
|
+
pi.options.allow_nan = Yes;
|
1072
|
+
pi.options.nilnil = Yes;
|
1073
|
+
oj_set_custom_callbacks(&pi);
|
1074
|
+
|
1075
|
+
if (T_STRING == rb_type(*argv)) {
|
1076
|
+
return oj_pi_parse(argc, argv, &pi, 0, 0, false);
|
1077
|
+
} else {
|
1078
|
+
return oj_pi_sparse(argc, argv, &pi, 0);
|
1079
|
+
}
|
1080
|
+
}
|
1081
|
+
|
1082
|
+
VALUE
|
1083
|
+
oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
|
1084
|
+
struct _ParseInfo pi;
|
1085
|
+
|
1086
|
+
parse_info_init(&pi);
|
1087
|
+
pi.options = oj_default_options;
|
1088
|
+
pi.handler = Qnil;
|
1089
|
+
pi.err_class = Qnil;
|
1090
|
+
pi.max_depth = 0;
|
1091
|
+
pi.options.allow_nan = Yes;
|
1092
|
+
pi.options.nilnil = Yes;
|
1093
|
+
oj_set_custom_callbacks(&pi);
|
1094
|
+
pi.end_hash = end_hash;
|
1095
|
+
|
1096
|
+
return oj_pi_parse(argc, argv, &pi, json, len, false);
|
1097
|
+
}
|