oj 2.18.3 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +100 -83
- data/ext/oj/dump_custom.c +759 -0
- data/ext/oj/oj.c +10 -2
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parse.c +7 -0
- data/lib/oj/version.rb +1 -1
- data/test/foo.rb +2 -19
- data/test/isolated/shared.rb +10 -4
- data/test/isolated/test_mimic_rails_after.rb +3 -3
- data/test/isolated/test_mimic_rails_before.rb +3 -3
- data/test/mem.rb +20 -0
- data/test/omit.rb +20 -0
- data/test/rails_datetime_test.rb +24 -0
- data/test/test_custom.rb +399 -0
- data/test/test_various.rb +22 -0
- data/test/test_writer.rb +16 -0
- data/test/x_test.rb +185 -0
- metadata +72 -69
- 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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 875790efa9c4efd0fd2cc2e1b6ae5d09ffb55541
|
|
4
|
+
data.tar.gz: 3f321cf28069efd50b6f168e55e86445f2987f40
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b42a692164cecf6b9767b2089d6699bb90fa54fa875be97d55a464f9e26c8455c71cc6b5df964473b941237c5784b247cb5c31c367206b593df007ecaa8eb7a
|
|
7
|
+
data.tar.gz: 49861fedc710aa30613c11f3e4ac521caea95355ffc917ea23ab2812a730e200ae3742d927ac693bd7b947032f21750482ab8a6fadd44d3307f06d1f646b404b
|
data/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Oj gem
|
|
2
|
-
[](http://travis-ci.org/ohler55/oj?branch=master)  
|
|
3
3
|
|
|
4
4
|
A fast JSON parser and Object marshaller as a Ruby gem.
|
|
5
5
|
|
|
@@ -74,110 +74,127 @@ actual default options but on a copy of the Hash:
|
|
|
74
74
|
Oj.default_options = {:mode => :compat }
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
### Common (for serializer and parser) options
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
match the Ruby `Object`'s variable names without the '@' prefix
|
|
81
|
-
character. This mode has the best performance and is the default.
|
|
79
|
+
* `:mode` [Symbol] mode for dumping and loading JSON. **Important format details [here](http://www.ohler.com/dev/oj_misc/encoding_format.html)**
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
- `:object` mode will dump any `Object` as a JSON `Object` with keys that
|
|
82
|
+
match the Ruby `Object`'s variable names without the '@' prefix
|
|
83
|
+
character. This mode has the best performance and is the default
|
|
85
84
|
|
|
86
|
-
|
|
85
|
+
- `:strict` mode will only allow the 7 basic JSON types to be serialized. Any
|
|
86
|
+
other `Object` will raise an `Exception`
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
- `:null` mode replaces any `Object` that is not one of the JSON types with a JSON `null`
|
|
89
|
+
|
|
90
|
+
- `:compat` mode attempts to be compatible with other systems. It will
|
|
91
|
+
serialize any `Object`, but will check to see if the `Object` implements an
|
|
92
|
+
`to_hash` or `to_json` method. If either exists, that method is used for
|
|
93
|
+
serializing the `Object`. Since `as_json` is more flexible and produces
|
|
94
|
+
more consistent output, it is preferred over the `to_json` method. If
|
|
95
|
+
neither the `to_json` or `to_hash` methods exists, then the Oj internal
|
|
96
|
+
`Object` variable encoding is used
|
|
97
|
+
|
|
98
|
+
* `:time_format` [Symbol] time format when dumping in :compat and :object mode
|
|
99
|
+
|
|
100
|
+
- `:unix` time is output as a decimal number in seconds since epoch including
|
|
101
|
+
fractions of a second.
|
|
102
|
+
|
|
103
|
+
- `:unix_zone` similar to the `:unix` format but with the timezone encoded in
|
|
104
|
+
the exponent of the decimal number of seconds since epoch.
|
|
105
|
+
|
|
106
|
+
- `:xmlschema` time is output as a string that follows the XML schema definition.
|
|
107
|
+
|
|
108
|
+
- `:ruby` time is output as a string formatted using the Ruby `to_s` conversion.
|
|
109
|
+
|
|
110
|
+
* `:second_precision` [Fixnum] number of digits after the decimal when dumping
|
|
111
|
+
the seconds portion of time
|
|
112
|
+
|
|
113
|
+
* `:bigdecimal_as_decimal` [Boolean] dump BigDecimal as a decimal number
|
|
114
|
+
or as a String
|
|
115
|
+
|
|
116
|
+
* `:create_id` [String] create id for json compatible object encoding,
|
|
117
|
+
default is `json_create`.
|
|
118
|
+
|
|
119
|
+
* `:allow_gc` [Boolean] allow or prohibit GC during parsing, default is
|
|
120
|
+
true (allow).
|
|
121
|
+
|
|
122
|
+
* `:quirks_mode` [Boolean] Allow single JSON values instead of
|
|
123
|
+
documents, default is true (allow).
|
|
124
|
+
|
|
125
|
+
* `:allow_invalid_unicode` [Boolean] Allow invalid unicode, default is
|
|
126
|
+
false (don't allow)
|
|
127
|
+
|
|
128
|
+
* `:omit_nil` [Boolean] If true, Hash and Object attributes with nil values
|
|
129
|
+
are omitted
|
|
130
|
+
|
|
131
|
+
### Serializer options
|
|
95
132
|
|
|
96
133
|
* `:indent` [Fixnum] number of spaces to indent each element in an JSON
|
|
97
134
|
document, zero is no newline between JSON elements, negative indicates no
|
|
98
135
|
newline between top level JSON elements in a stream
|
|
99
136
|
|
|
100
|
-
* `:circular` [Boolean] support circular references while dumping
|
|
101
|
-
|
|
137
|
+
* `:circular` [Boolean] support circular references while dumping
|
|
138
|
+
|
|
139
|
+
* `:escape_mode` [Symbol] determines the characters to escape
|
|
140
|
+
|
|
141
|
+
- `:newline` allows unescaped newlines in the output
|
|
142
|
+
|
|
143
|
+
- `:json` follows the JSON specification. This is the default mode
|
|
144
|
+
|
|
145
|
+
- `:xss_safe` escapes HTML and XML characters such as `&` and `<`
|
|
146
|
+
|
|
147
|
+
- `:ascii` escapes all non-ascii or characters with the hi-bit set
|
|
148
|
+
|
|
149
|
+
* `:use_to_json` [Boolean] call `to_json()` methods on dump, default is
|
|
150
|
+
false
|
|
151
|
+
|
|
152
|
+
* `:use_as_json` [Boolean] call `as_json()` methods on dump, default is
|
|
153
|
+
false
|
|
154
|
+
|
|
155
|
+
* `:nan` [Symbol] How to dump Infinity, -Infinity, and
|
|
156
|
+
NaN in :null, :strict, and :compat mode. Default is :auto
|
|
157
|
+
|
|
158
|
+
- `:null` places a null
|
|
159
|
+
|
|
160
|
+
- `:huge` places a huge number
|
|
161
|
+
|
|
162
|
+
- `:word` places Infinity or NaN
|
|
163
|
+
|
|
164
|
+
- `:raise` raises and exception
|
|
165
|
+
|
|
166
|
+
- `:auto` uses default for each mode which are `:raise` for `:strict`, `:null` for `:null`, and `:word` for `:compat`
|
|
167
|
+
|
|
168
|
+
### Parser options
|
|
169
|
+
|
|
102
170
|
* `:auto_define` [Boolean] automatically define classes if they do not
|
|
103
|
-
exist
|
|
104
|
-
|
|
105
|
-
* `:symbol_keys` [Boolean] use symbols instead of strings for hash keys.
|
|
106
|
-
|
|
107
|
-
* `:escape_mode` [Symbol] determines the characters to escape.
|
|
171
|
+
exist
|
|
108
172
|
|
|
109
|
-
|
|
173
|
+
* `:symbol_keys` [Boolean] use symbols instead of strings for hash keys
|
|
110
174
|
|
|
111
|
-
- `:json` follows the JSON specification. This is the default mode.
|
|
112
|
-
|
|
113
|
-
- `:xss_safe` escapes HTML and XML characters such as `&` and `<`.
|
|
114
|
-
|
|
115
|
-
- `:ascii` escapes all non-ascii or characters with the hi-bit set.
|
|
116
|
-
|
|
117
175
|
* `:class_cache` [Boolean] cache classes for faster parsing (if
|
|
118
176
|
dynamically modifying classes or reloading classes then don't use this)
|
|
119
|
-
|
|
120
|
-
* `:time_format` [Symbol] time format when dumping in :compat and :object mode
|
|
121
|
-
|
|
122
|
-
- `:unix` time is output as a decimal number in seconds since epoch including
|
|
123
|
-
fractions of a second.
|
|
124
|
-
|
|
125
|
-
- `:unix_zone` similar to the `:unix` format but with the timezone encoded in
|
|
126
|
-
the exponent of the decimal number of seconds since epoch.
|
|
127
|
-
|
|
128
|
-
- `:xmlschema` time is output as a string that follows the XML schema definition.
|
|
129
|
-
|
|
130
|
-
- `:ruby` time is output as a string formatted using the Ruby `to_s` conversion.
|
|
131
177
|
|
|
132
|
-
* `:bigdecimal_as_decimal` [Boolean] dump BigDecimal as a decimal number
|
|
133
|
-
or as a String
|
|
134
|
-
|
|
135
178
|
* `:bigdecimal_load` [Symbol] load decimals as BigDecimal instead of as a
|
|
136
|
-
Float
|
|
179
|
+
Float
|
|
180
|
+
|
|
181
|
+
- `:bigdecimal` convert all decimal numbers to BigDecimal
|
|
182
|
+
|
|
183
|
+
- `:float` convert all decimal numbers to Float
|
|
184
|
+
|
|
185
|
+
- `:auto` the most precise for the number of digits is used
|
|
137
186
|
|
|
138
|
-
- `:bigdecimal` convert all decimal numbers to BigDecimal.
|
|
139
|
-
|
|
140
|
-
- `:float` convert all decimal numbers to Float.
|
|
141
|
-
|
|
142
|
-
- `:auto` the most precise for the number of digits is used.
|
|
143
|
-
|
|
144
|
-
* `:create_id` [String] create id for json compatible object encoding,
|
|
145
|
-
default is `json_create`.
|
|
146
|
-
|
|
147
|
-
* `:second_precision` [Fixnum] number of digits after the decimal when dumping
|
|
148
|
-
the seconds portion of time
|
|
149
|
-
|
|
150
187
|
* `:float_precision` [Fixnum] number of digits of precision when dumping
|
|
151
188
|
floats, 0 indicates use Ruby
|
|
152
|
-
|
|
153
|
-
* `:use_to_json` [Boolean] call to_json() methods on dump, default is
|
|
154
|
-
false
|
|
155
|
-
|
|
156
|
-
* `:use_as_json` [Boolean] call as_json() methods on dump, default is
|
|
157
|
-
false
|
|
158
|
-
|
|
189
|
+
|
|
159
190
|
* `:nilnil` [Boolean] if true a nil input to load will return nil and
|
|
160
191
|
not raise an Exception
|
|
161
|
-
|
|
162
|
-
* `:allow_gc` [Boolean] allow or prohibit GC during parsing, default is
|
|
163
|
-
true (allow).
|
|
164
|
-
|
|
165
|
-
* `:quirks_mode` [Boolean] Allow single JSON values instead of
|
|
166
|
-
documents, default is true (allow).
|
|
167
192
|
|
|
168
|
-
* `:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
default for each mode which are :raise for :strict, :null for :null, and :word
|
|
172
|
-
for :compat. Default is :auto.
|
|
173
|
-
|
|
174
|
-
* `:allow_invalid_unicode` [Boolean] Allow invalid unicode, default is
|
|
175
|
-
false (don't allow)
|
|
176
|
-
|
|
177
|
-
* `:hash_class` [Class] Class to use instead of Hash on load
|
|
193
|
+
* `:empty_string` [Boolean] if true an empty input will not raise an
|
|
194
|
+
Exception, default is true (allow). When Oj.mimic_JSON is used,
|
|
195
|
+
default is false (raise exception when empty string is encountered)
|
|
178
196
|
|
|
179
|
-
* `:
|
|
180
|
-
are omitted
|
|
197
|
+
* `:hash_class` [Class] Class to use instead of Hash on load
|
|
181
198
|
|
|
182
199
|
## Releases
|
|
183
200
|
|
|
@@ -187,7 +204,7 @@ See [CHANGELOG.md](CHANGELOG.md)
|
|
|
187
204
|
|
|
188
205
|
**Ruby**
|
|
189
206
|
|
|
190
|
-
Oj is compatible with Ruby 1.
|
|
207
|
+
Oj is compatible with Ruby 1.9.3, 2.0.0, 2.1, 2.2, 2.3, 2.4 and RBX.
|
|
191
208
|
Support for JRuby has been removed as JRuby no longer supports C extensions and
|
|
192
209
|
there are bugs in the older versions that are not being fixed.
|
|
193
210
|
|
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
/* dump_object.c
|
|
2
|
+
* Copyright (c) 2012, 2017, Peter Ohler
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#include "dump.h"
|
|
7
|
+
|
|
8
|
+
static void
|
|
9
|
+
dump_time(VALUE obj, Out out) {
|
|
10
|
+
switch (out->opts->time_format) {
|
|
11
|
+
case RubyTime: oj_dump_obj_to_s(obj, out); break;
|
|
12
|
+
case XmlTime: oj_dump_xml_time(obj, out); break;
|
|
13
|
+
case UnixZTime: oj_dump_time(obj, out, 1); break;
|
|
14
|
+
case UnixTime:
|
|
15
|
+
default: oj_dump_time(obj, out, 0); break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static int
|
|
20
|
+
hash_cb(VALUE key, VALUE value, Out out) {
|
|
21
|
+
int depth = out->depth;
|
|
22
|
+
|
|
23
|
+
if (out->omit_nil && Qnil == value) {
|
|
24
|
+
return ST_CONTINUE;
|
|
25
|
+
}
|
|
26
|
+
if (!out->opts->dump_opts.use) {
|
|
27
|
+
assure_size(out, depth * out->indent + 1);
|
|
28
|
+
fill_indent(out, depth);
|
|
29
|
+
} else {
|
|
30
|
+
assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
|
|
31
|
+
if (0 < out->opts->dump_opts.hash_size) {
|
|
32
|
+
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
|
33
|
+
out->cur += out->opts->dump_opts.hash_size;
|
|
34
|
+
}
|
|
35
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
|
36
|
+
int i;
|
|
37
|
+
for (i = depth; 0 < i; i--) {
|
|
38
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
|
39
|
+
out->cur += out->opts->dump_opts.indent_size;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
switch (rb_type(key)) {
|
|
44
|
+
case T_STRING:
|
|
45
|
+
oj_dump_str(key, 0, out, false);
|
|
46
|
+
break;
|
|
47
|
+
case T_SYMBOL:
|
|
48
|
+
oj_dump_sym(key, 0, out, false);
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
/*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
|
|
52
|
+
oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (!out->opts->dump_opts.use) {
|
|
56
|
+
*out->cur++ = ':';
|
|
57
|
+
} else {
|
|
58
|
+
assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
|
|
59
|
+
if (0 < out->opts->dump_opts.before_size) {
|
|
60
|
+
strcpy(out->cur, out->opts->dump_opts.before_sep);
|
|
61
|
+
out->cur += out->opts->dump_opts.before_size;
|
|
62
|
+
}
|
|
63
|
+
*out->cur++ = ':';
|
|
64
|
+
if (0 < out->opts->dump_opts.after_size) {
|
|
65
|
+
strcpy(out->cur, out->opts->dump_opts.after_sep);
|
|
66
|
+
out->cur += out->opts->dump_opts.after_size;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
oj_dump_compat_val(value, depth, out, true);
|
|
70
|
+
out->depth = depth;
|
|
71
|
+
*out->cur++ = ',';
|
|
72
|
+
|
|
73
|
+
return ST_CONTINUE;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static void
|
|
77
|
+
dump_hash_class(VALUE obj, VALUE clas, int depth, Out out) {
|
|
78
|
+
int cnt;
|
|
79
|
+
size_t size;
|
|
80
|
+
|
|
81
|
+
cnt = (int)RHASH_SIZE(obj);
|
|
82
|
+
size = depth * out->indent + 2;
|
|
83
|
+
assure_size(out, 2);
|
|
84
|
+
if (0 == cnt) {
|
|
85
|
+
*out->cur++ = '{';
|
|
86
|
+
*out->cur++ = '}';
|
|
87
|
+
} else {
|
|
88
|
+
long id = oj_check_circular(obj, out);
|
|
89
|
+
|
|
90
|
+
if (0 > id) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
*out->cur++ = '{';
|
|
94
|
+
if (0 < id) {
|
|
95
|
+
assure_size(out, size + 16);
|
|
96
|
+
fill_indent(out, depth + 1);
|
|
97
|
+
*out->cur++ = '"';
|
|
98
|
+
*out->cur++ = '^';
|
|
99
|
+
*out->cur++ = 'i';
|
|
100
|
+
*out->cur++ = '"';
|
|
101
|
+
*out->cur++ = ':';
|
|
102
|
+
dump_ulong(id, out);
|
|
103
|
+
*out->cur++ = ',';
|
|
104
|
+
}
|
|
105
|
+
out->depth = depth + 1;
|
|
106
|
+
rb_hash_foreach(obj, hash_cb, (VALUE)out);
|
|
107
|
+
if (',' == *(out->cur - 1)) {
|
|
108
|
+
out->cur--; // backup to overwrite last comma
|
|
109
|
+
}
|
|
110
|
+
if (!out->opts->dump_opts.use) {
|
|
111
|
+
assure_size(out, size);
|
|
112
|
+
fill_indent(out, depth);
|
|
113
|
+
} else {
|
|
114
|
+
size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
|
|
115
|
+
assure_size(out, size);
|
|
116
|
+
if (0 < out->opts->dump_opts.hash_size) {
|
|
117
|
+
strcpy(out->cur, out->opts->dump_opts.hash_nl);
|
|
118
|
+
out->cur += out->opts->dump_opts.hash_size;
|
|
119
|
+
}
|
|
120
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
|
121
|
+
int i;
|
|
122
|
+
|
|
123
|
+
for (i = depth; 0 < i; i--) {
|
|
124
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
|
125
|
+
out->cur += out->opts->dump_opts.indent_size;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
*out->cur++ = '}';
|
|
130
|
+
}
|
|
131
|
+
*out->cur = '\0';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static void
|
|
135
|
+
dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
|
|
136
|
+
dump_hash_class(obj, rb_obj_class(obj), depth, out);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#if HAS_IVAR_HELPERS
|
|
140
|
+
static int
|
|
141
|
+
dump_attr_cb(ID key, VALUE value, Out out) {
|
|
142
|
+
int depth = out->depth;
|
|
143
|
+
size_t size = depth * out->indent + 1;
|
|
144
|
+
const char *attr = rb_id2name(key);
|
|
145
|
+
|
|
146
|
+
if (out->omit_nil && Qnil == value) {
|
|
147
|
+
return ST_CONTINUE;
|
|
148
|
+
}
|
|
149
|
+
#if HAS_EXCEPTION_MAGIC
|
|
150
|
+
if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
|
|
151
|
+
return ST_CONTINUE;
|
|
152
|
+
}
|
|
153
|
+
#endif
|
|
154
|
+
assure_size(out, size);
|
|
155
|
+
fill_indent(out, depth);
|
|
156
|
+
if ('@' == *attr) {
|
|
157
|
+
attr++;
|
|
158
|
+
oj_dump_cstr(attr, strlen(attr), 0, 0, out);
|
|
159
|
+
} else {
|
|
160
|
+
char buf[32];
|
|
161
|
+
|
|
162
|
+
*buf = '~';
|
|
163
|
+
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
|
164
|
+
buf[sizeof(buf) - 1] = '\0';
|
|
165
|
+
oj_dump_cstr(buf, strlen(buf), 0, 0, out);
|
|
166
|
+
}
|
|
167
|
+
*out->cur++ = ':';
|
|
168
|
+
oj_dump_comp_val(value, depth, out, 0, 0, true);
|
|
169
|
+
out->depth = depth;
|
|
170
|
+
*out->cur++ = ',';
|
|
171
|
+
|
|
172
|
+
return ST_CONTINUE;
|
|
173
|
+
}
|
|
174
|
+
#endif
|
|
175
|
+
|
|
176
|
+
static void
|
|
177
|
+
dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
|
178
|
+
size_t size = 0;
|
|
179
|
+
int d2 = depth + 1;
|
|
180
|
+
int cnt;
|
|
181
|
+
|
|
182
|
+
assure_size(out, 2);
|
|
183
|
+
*out->cur++ = '{';
|
|
184
|
+
{
|
|
185
|
+
#if HAS_IVAR_HELPERS
|
|
186
|
+
cnt = (int)rb_ivar_count(obj);
|
|
187
|
+
#else
|
|
188
|
+
volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
|
|
189
|
+
VALUE *np = RARRAY_PTR(vars);
|
|
190
|
+
ID vid;
|
|
191
|
+
const char *attr;
|
|
192
|
+
int i;
|
|
193
|
+
int first = 1;
|
|
194
|
+
|
|
195
|
+
cnt = (int)RARRAY_LEN(vars);
|
|
196
|
+
#endif
|
|
197
|
+
if (Qundef != clas && 0 < cnt) {
|
|
198
|
+
*out->cur++ = ',';
|
|
199
|
+
}
|
|
200
|
+
if (0 == cnt && Qundef == clas) {
|
|
201
|
+
// Might be something special like an Enumerable.
|
|
202
|
+
if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
|
|
203
|
+
out->cur--;
|
|
204
|
+
oj_dump_compat_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out, false);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
out->depth = depth + 1;
|
|
209
|
+
#if HAS_IVAR_HELPERS
|
|
210
|
+
rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
|
|
211
|
+
if (',' == *(out->cur - 1)) {
|
|
212
|
+
out->cur--; // backup to overwrite last comma
|
|
213
|
+
}
|
|
214
|
+
#else
|
|
215
|
+
size = d2 * out->indent + 1;
|
|
216
|
+
for (i = cnt; 0 < i; i--, np++) {
|
|
217
|
+
VALUE value;
|
|
218
|
+
|
|
219
|
+
vid = rb_to_id(*np);
|
|
220
|
+
attr = rb_id2name(vid);
|
|
221
|
+
value = rb_ivar_get(obj, vid);
|
|
222
|
+
if (out->omit_nil && Qnil == value) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (first) {
|
|
226
|
+
first = 0;
|
|
227
|
+
} else {
|
|
228
|
+
*out->cur++ = ',';
|
|
229
|
+
}
|
|
230
|
+
assure_size(out, size);
|
|
231
|
+
fill_indent(out, d2);
|
|
232
|
+
if ('@' == *attr) {
|
|
233
|
+
attr++;
|
|
234
|
+
oj_dump_cstr(attr, strlen(attr), 0, 0, out);
|
|
235
|
+
} else {
|
|
236
|
+
char buf[32];
|
|
237
|
+
|
|
238
|
+
*buf = '~';
|
|
239
|
+
strncpy(buf + 1, attr, sizeof(buf) - 2);
|
|
240
|
+
buf[sizeof(buf) - 1] = '\0';
|
|
241
|
+
oj_dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
|
|
242
|
+
}
|
|
243
|
+
*out->cur++ = ':';
|
|
244
|
+
oj_dump_compat_val(value, d2, out, 0, 0, true);
|
|
245
|
+
assure_size(out, 2);
|
|
246
|
+
}
|
|
247
|
+
#endif
|
|
248
|
+
#if HAS_EXCEPTION_MAGIC
|
|
249
|
+
if (rb_obj_is_kind_of(obj, rb_eException)) {
|
|
250
|
+
volatile VALUE rv;
|
|
251
|
+
|
|
252
|
+
if (',' != *(out->cur - 1)) {
|
|
253
|
+
*out->cur++ = ',';
|
|
254
|
+
}
|
|
255
|
+
// message
|
|
256
|
+
assure_size(out, 2);
|
|
257
|
+
fill_indent(out, d2);
|
|
258
|
+
oj_dump_cstr("~mesg", 5, 0, 0, out);
|
|
259
|
+
*out->cur++ = ':';
|
|
260
|
+
rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
|
|
261
|
+
oj_dump_compat_val(rv, d2, out, true);
|
|
262
|
+
assure_size(out, size + 2);
|
|
263
|
+
*out->cur++ = ',';
|
|
264
|
+
// backtrace
|
|
265
|
+
fill_indent(out, d2);
|
|
266
|
+
oj_dump_cstr("~bt", 3, 0, 0, out);
|
|
267
|
+
*out->cur++ = ':';
|
|
268
|
+
rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
|
|
269
|
+
oj_dump_compat_val(rv, d2, out, true);
|
|
270
|
+
assure_size(out, 2);
|
|
271
|
+
}
|
|
272
|
+
#endif
|
|
273
|
+
out->depth = depth;
|
|
274
|
+
}
|
|
275
|
+
fill_indent(out, depth);
|
|
276
|
+
*out->cur++ = '}';
|
|
277
|
+
*out->cur = '\0';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static void
|
|
281
|
+
dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
|
282
|
+
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
|
283
|
+
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
|
284
|
+
|
|
285
|
+
if (T_HASH != rb_type(h)) {
|
|
286
|
+
// It seems that ActiveRecord implemented to_hash so that it returns
|
|
287
|
+
// an Array and not a Hash. To get around that any value returned
|
|
288
|
+
// will be dumped.
|
|
289
|
+
|
|
290
|
+
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
|
291
|
+
oj_dump_compat_val(h, depth, out, false);
|
|
292
|
+
} else {
|
|
293
|
+
dump_hash_class(h, Qundef, depth, out);
|
|
294
|
+
}
|
|
295
|
+
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
|
296
|
+
volatile VALUE aj;
|
|
297
|
+
|
|
298
|
+
#if HAS_METHOD_ARITY
|
|
299
|
+
// Some classes elect to not take an options argument so check the arity
|
|
300
|
+
// of as_json.
|
|
301
|
+
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
|
302
|
+
case 0:
|
|
303
|
+
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
|
304
|
+
break;
|
|
305
|
+
case 1:
|
|
306
|
+
if (1 <= out->argc) {
|
|
307
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, (VALUE*)out->argv);
|
|
308
|
+
} else {
|
|
309
|
+
VALUE nothing [1];
|
|
310
|
+
|
|
311
|
+
nothing[0] = Qnil;
|
|
312
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
default:
|
|
316
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
#else
|
|
320
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
321
|
+
#endif
|
|
322
|
+
// Catch the obvious brain damaged recursive dumping.
|
|
323
|
+
if (aj == obj) {
|
|
324
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
325
|
+
|
|
326
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), false, false, out);
|
|
327
|
+
} else {
|
|
328
|
+
oj_dump_compat_val(aj, depth, out, false);
|
|
329
|
+
}
|
|
330
|
+
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
|
331
|
+
volatile VALUE rs;
|
|
332
|
+
const char *s;
|
|
333
|
+
int len;
|
|
334
|
+
|
|
335
|
+
rs = rb_funcall(obj, oj_to_json_id, 0);
|
|
336
|
+
s = rb_string_value_ptr((VALUE*)&rs);
|
|
337
|
+
len = (int)RSTRING_LEN(rs);
|
|
338
|
+
|
|
339
|
+
assure_size(out, len + 1);
|
|
340
|
+
memcpy(out->cur, s, len);
|
|
341
|
+
out->cur += len;
|
|
342
|
+
*out->cur = '\0';
|
|
343
|
+
} else {
|
|
344
|
+
VALUE clas = rb_obj_class(obj);
|
|
345
|
+
|
|
346
|
+
if (oj_bigdecimal_class == clas) {
|
|
347
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
348
|
+
const char *str = rb_string_value_ptr((VALUE*)&rstr);
|
|
349
|
+
int len = RSTRING_LEN(rstr);
|
|
350
|
+
|
|
351
|
+
if (Yes == out->opts->bigdec_as_num) {
|
|
352
|
+
oj_dump_raw(str, len, out);
|
|
353
|
+
} else if (0 == strcasecmp("Infinity", str)) {
|
|
354
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
|
|
355
|
+
oj_dump_raw(str, len, out);
|
|
356
|
+
} else if (0 == strcasecmp("-Infinity", str)) {
|
|
357
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
|
|
358
|
+
oj_dump_raw(str, len, out);
|
|
359
|
+
} else {
|
|
360
|
+
oj_dump_cstr(str, len, 0, 0, out);
|
|
361
|
+
}
|
|
362
|
+
#if (defined T_RATIONAL && defined RRATIONAL)
|
|
363
|
+
} else if (oj_datetime_class == clas || oj_date_class == clas || rb_cRational == clas) {
|
|
364
|
+
#else
|
|
365
|
+
} else if (oj_datetime_class == clas || oj_date_class == clas) {
|
|
366
|
+
#endif
|
|
367
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
368
|
+
|
|
369
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
370
|
+
} else {
|
|
371
|
+
dump_obj_attrs(obj, Qundef, 0, depth, out);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
*out->cur = '\0';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
static void
|
|
378
|
+
dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
379
|
+
size_t size;
|
|
380
|
+
int i, cnt;
|
|
381
|
+
int d2 = depth + 1;
|
|
382
|
+
long id = oj_check_circular(a, out);
|
|
383
|
+
|
|
384
|
+
if (id < 0) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
cnt = (int)RARRAY_LEN(a);
|
|
388
|
+
*out->cur++ = '[';
|
|
389
|
+
if (0 < id) {
|
|
390
|
+
oj_dump_nil(Qnil, 0, out, false);
|
|
391
|
+
}
|
|
392
|
+
size = 2;
|
|
393
|
+
assure_size(out, size);
|
|
394
|
+
if (0 == cnt) {
|
|
395
|
+
*out->cur++ = ']';
|
|
396
|
+
} else {
|
|
397
|
+
if (0 < id) {
|
|
398
|
+
*out->cur++ = ',';
|
|
399
|
+
}
|
|
400
|
+
if (out->opts->dump_opts.use) {
|
|
401
|
+
size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
|
|
402
|
+
} else {
|
|
403
|
+
size = d2 * out->indent + 2;
|
|
404
|
+
}
|
|
405
|
+
cnt--;
|
|
406
|
+
for (i = 0; i <= cnt; i++) {
|
|
407
|
+
assure_size(out, size);
|
|
408
|
+
if (out->opts->dump_opts.use) {
|
|
409
|
+
if (0 < out->opts->dump_opts.array_size) {
|
|
410
|
+
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
|
411
|
+
out->cur += out->opts->dump_opts.array_size;
|
|
412
|
+
}
|
|
413
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
|
414
|
+
int i;
|
|
415
|
+
for (i = d2; 0 < i; i--) {
|
|
416
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
|
417
|
+
out->cur += out->opts->dump_opts.indent_size;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
fill_indent(out, d2);
|
|
422
|
+
}
|
|
423
|
+
oj_dump_comp_val(rb_ary_entry(a, i), d2, out, 0, 0, true);
|
|
424
|
+
if (i < cnt) {
|
|
425
|
+
*out->cur++ = ',';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
size = depth * out->indent + 1;
|
|
429
|
+
assure_size(out, size);
|
|
430
|
+
if (out->opts->dump_opts.use) {
|
|
431
|
+
//printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
|
|
432
|
+
if (0 < out->opts->dump_opts.array_size) {
|
|
433
|
+
strcpy(out->cur, out->opts->dump_opts.array_nl);
|
|
434
|
+
out->cur += out->opts->dump_opts.array_size;
|
|
435
|
+
}
|
|
436
|
+
if (0 < out->opts->dump_opts.indent_size) {
|
|
437
|
+
int i;
|
|
438
|
+
|
|
439
|
+
for (i = depth; 0 < i; i--) {
|
|
440
|
+
strcpy(out->cur, out->opts->dump_opts.indent_str);
|
|
441
|
+
out->cur += out->opts->dump_opts.indent_size;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
fill_indent(out, depth);
|
|
446
|
+
}
|
|
447
|
+
*out->cur++ = ']';
|
|
448
|
+
}
|
|
449
|
+
*out->cur = '\0';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
static void
|
|
453
|
+
dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
|
|
454
|
+
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
|
455
|
+
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
|
456
|
+
|
|
457
|
+
if (T_HASH != rb_type(h)) {
|
|
458
|
+
// It seems that ActiveRecord implemented to_hash so that it
|
|
459
|
+
// returns an Array and not a Hash. To get around that any value
|
|
460
|
+
// returned will be dumped.
|
|
461
|
+
|
|
462
|
+
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
|
463
|
+
oj_dump_comp_val(h, depth, out, 0, 0, false);
|
|
464
|
+
}
|
|
465
|
+
dump_hash_class(h, Qundef, depth, out);
|
|
466
|
+
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
|
467
|
+
volatile VALUE aj;
|
|
468
|
+
|
|
469
|
+
#if HAS_METHOD_ARITY
|
|
470
|
+
// Some classes elect to not take an options argument so check the arity
|
|
471
|
+
// of as_json.
|
|
472
|
+
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
|
473
|
+
case 0:
|
|
474
|
+
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
|
475
|
+
break;
|
|
476
|
+
case 1:
|
|
477
|
+
if (1 <= out->argc) {
|
|
478
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, out->argv);
|
|
479
|
+
} else {
|
|
480
|
+
VALUE nothing [1];
|
|
481
|
+
|
|
482
|
+
nothing[0] = Qnil;
|
|
483
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
default:
|
|
487
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
#else
|
|
491
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
492
|
+
#endif
|
|
493
|
+
// Catch the obvious brain damaged recursive dumping.
|
|
494
|
+
if (aj == obj) {
|
|
495
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
496
|
+
|
|
497
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), false, false, out);
|
|
498
|
+
} else {
|
|
499
|
+
oj_dump_compat_val(aj, depth, out, false);
|
|
500
|
+
}
|
|
501
|
+
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
|
502
|
+
volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
|
|
503
|
+
const char *s;
|
|
504
|
+
int len;
|
|
505
|
+
|
|
506
|
+
s = rb_string_value_ptr((VALUE*)&rs);
|
|
507
|
+
len = (int)RSTRING_LEN(rs);
|
|
508
|
+
assure_size(out, len);
|
|
509
|
+
memcpy(out->cur, s, len);
|
|
510
|
+
out->cur += len;
|
|
511
|
+
} else {
|
|
512
|
+
VALUE clas = rb_obj_class(obj);
|
|
513
|
+
VALUE ma = Qnil;
|
|
514
|
+
VALUE v;
|
|
515
|
+
char num_id[32];
|
|
516
|
+
int i;
|
|
517
|
+
int d2 = depth + 1;
|
|
518
|
+
int d3 = d2 + 1;
|
|
519
|
+
size_t size = d2 * out->indent + d3 * out->indent + 3;
|
|
520
|
+
const char *name;
|
|
521
|
+
int cnt;
|
|
522
|
+
size_t len;
|
|
523
|
+
|
|
524
|
+
assure_size(out, size);
|
|
525
|
+
if (clas == rb_cRange) {
|
|
526
|
+
*out->cur++ = '"';
|
|
527
|
+
oj_dump_comp_val(rb_funcall(obj, oj_begin_id, 0), d3, out, 0, 0, false);
|
|
528
|
+
assure_size(out, 3);
|
|
529
|
+
*out->cur++ = '.';
|
|
530
|
+
*out->cur++ = '.';
|
|
531
|
+
if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
|
|
532
|
+
*out->cur++ = '.';
|
|
533
|
+
}
|
|
534
|
+
oj_dump_comp_val(rb_funcall(obj, oj_end_id, 0), d3, out, 0, 0, false);
|
|
535
|
+
*out->cur++ = '"';
|
|
536
|
+
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
*out->cur++ = '{';
|
|
540
|
+
fill_indent(out, d2);
|
|
541
|
+
size = d3 * out->indent + 2;
|
|
542
|
+
#if HAS_STRUCT_MEMBERS
|
|
543
|
+
ma = rb_struct_s_members(clas);
|
|
544
|
+
#endif
|
|
545
|
+
|
|
546
|
+
#ifdef RSTRUCT_LEN
|
|
547
|
+
#if UNIFY_FIXNUM_AND_BIGNUM
|
|
548
|
+
cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
|
|
549
|
+
#else // UNIFY_FIXNUM_AND_INTEGER
|
|
550
|
+
cnt = (int)RSTRUCT_LEN(obj);
|
|
551
|
+
#endif // UNIFY_FIXNUM_AND_INTEGER
|
|
552
|
+
#else
|
|
553
|
+
// This is a bit risky as a struct in C ruby is not the same as a Struct
|
|
554
|
+
// class in interpreted Ruby so length() may not be defined.
|
|
555
|
+
cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
|
|
556
|
+
#endif
|
|
557
|
+
for (i = 0; i < cnt; i++) {
|
|
558
|
+
#ifdef RSTRUCT_LEN
|
|
559
|
+
v = RSTRUCT_GET(obj, i);
|
|
560
|
+
#else
|
|
561
|
+
v = rb_struct_aref(obj, INT2FIX(i));
|
|
562
|
+
#endif
|
|
563
|
+
if (ma != Qnil) {
|
|
564
|
+
name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
|
|
565
|
+
len = strlen(name);
|
|
566
|
+
} else {
|
|
567
|
+
len = snprintf(num_id, sizeof(num_id), "%d", i);
|
|
568
|
+
name = num_id;
|
|
569
|
+
}
|
|
570
|
+
assure_size(out, size + len + 3);
|
|
571
|
+
fill_indent(out, d3);
|
|
572
|
+
*out->cur++ = '"';
|
|
573
|
+
memcpy(out->cur, name, len);
|
|
574
|
+
out->cur += len;
|
|
575
|
+
*out->cur++ = '"';
|
|
576
|
+
*out->cur++ = ':';
|
|
577
|
+
oj_dump_comp_val(v, d3, out, 0, 0, true);
|
|
578
|
+
*out->cur++ = ',';
|
|
579
|
+
}
|
|
580
|
+
out->cur--;
|
|
581
|
+
*out->cur++ = '}';
|
|
582
|
+
*out->cur = '\0';
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static void
|
|
587
|
+
dump_data(VALUE obj, int depth, Out out, bool as_ok) {
|
|
588
|
+
VALUE clas = rb_obj_class(obj);
|
|
589
|
+
|
|
590
|
+
if (as_ok && Yes == out->opts->to_json && rb_respond_to(obj, oj_to_hash_id)) {
|
|
591
|
+
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
|
|
592
|
+
|
|
593
|
+
if (T_HASH != rb_type(h)) {
|
|
594
|
+
// It seems that ActiveRecord implemented to_hash so that it returns
|
|
595
|
+
// an Array and not a Hash. To get around that any value returned
|
|
596
|
+
// will be dumped.
|
|
597
|
+
|
|
598
|
+
//rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
|
|
599
|
+
oj_dump_compat_val(h, depth, out, false);
|
|
600
|
+
}
|
|
601
|
+
dump_hash_class(h, Qundef, depth, out);
|
|
602
|
+
} else if (Yes == out->opts->bigdec_as_num && oj_bigdecimal_class == clas) {
|
|
603
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
604
|
+
const char *str = rb_string_value_ptr((VALUE*)&rstr);
|
|
605
|
+
int len = RSTRING_LEN(rstr);
|
|
606
|
+
|
|
607
|
+
if (0 == strcasecmp("Infinity", str)) {
|
|
608
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
|
|
609
|
+
oj_dump_raw(str, len, out);
|
|
610
|
+
} else if (0 == strcasecmp("-Infinity", str)) {
|
|
611
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
|
|
612
|
+
oj_dump_raw(str, len, out);
|
|
613
|
+
} else {
|
|
614
|
+
oj_dump_raw(str, len, out);
|
|
615
|
+
}
|
|
616
|
+
} else if (as_ok && Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
|
|
617
|
+
volatile VALUE aj;
|
|
618
|
+
|
|
619
|
+
#if HAS_METHOD_ARITY
|
|
620
|
+
// Some classes elect to not take an options argument so check the arity
|
|
621
|
+
// of as_json.
|
|
622
|
+
switch (rb_obj_method_arity(obj, oj_as_json_id)) {
|
|
623
|
+
case 0:
|
|
624
|
+
aj = rb_funcall2(obj, oj_as_json_id, 0, 0);
|
|
625
|
+
break;
|
|
626
|
+
case 1:
|
|
627
|
+
if (1 <= out->argc) {
|
|
628
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, out->argv);
|
|
629
|
+
} else {
|
|
630
|
+
VALUE nothing [1];
|
|
631
|
+
|
|
632
|
+
nothing[0] = Qnil;
|
|
633
|
+
aj = rb_funcall2(obj, oj_as_json_id, 1, nothing);
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
default:
|
|
637
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
#else
|
|
641
|
+
aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
|
|
642
|
+
#endif
|
|
643
|
+
// Catch the obvious brain damaged recursive dumping.
|
|
644
|
+
if (aj == obj) {
|
|
645
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
646
|
+
|
|
647
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
648
|
+
} else {
|
|
649
|
+
oj_dump_comp_val(aj, depth, out, 0, 0, false);
|
|
650
|
+
}
|
|
651
|
+
} else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
|
|
652
|
+
volatile VALUE rs;
|
|
653
|
+
const char *s;
|
|
654
|
+
int len;
|
|
655
|
+
|
|
656
|
+
rs = rb_funcall(obj, oj_to_json_id, 0);
|
|
657
|
+
s = rb_string_value_ptr((VALUE*)&rs);
|
|
658
|
+
len = (int)RSTRING_LEN(rs);
|
|
659
|
+
|
|
660
|
+
assure_size(out, len + 1);
|
|
661
|
+
memcpy(out->cur, s, len);
|
|
662
|
+
out->cur += len;
|
|
663
|
+
*out->cur = '\0';
|
|
664
|
+
} else {
|
|
665
|
+
if (rb_cTime == clas) {
|
|
666
|
+
dump_time(obj, out);
|
|
667
|
+
} else if (oj_bigdecimal_class == clas) {
|
|
668
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
669
|
+
const char *str = rb_string_value_ptr((VALUE*)&rstr);
|
|
670
|
+
int len = RSTRING_LEN(rstr);
|
|
671
|
+
|
|
672
|
+
if (0 == strcasecmp("Infinity", str)) {
|
|
673
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
|
|
674
|
+
oj_dump_raw(str, len, out);
|
|
675
|
+
} else if (0 == strcasecmp("-Infinity", str)) {
|
|
676
|
+
str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
|
|
677
|
+
oj_dump_raw(str, len, out);
|
|
678
|
+
} else {
|
|
679
|
+
oj_dump_cstr(str, len, 0, 0, out);
|
|
680
|
+
}
|
|
681
|
+
} else if (oj_datetime_class == clas) {
|
|
682
|
+
volatile VALUE rstr;
|
|
683
|
+
|
|
684
|
+
switch (out->opts->time_format) {
|
|
685
|
+
case XmlTime:
|
|
686
|
+
rstr = rb_funcall(obj, rb_intern("xmlschema"), 1, INT2FIX(out->opts->sec_prec));
|
|
687
|
+
break;
|
|
688
|
+
case UnixZTime:
|
|
689
|
+
case UnixTime:
|
|
690
|
+
case RubyTime:
|
|
691
|
+
default:
|
|
692
|
+
rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
693
|
+
}
|
|
694
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
695
|
+
} else {
|
|
696
|
+
volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
|
697
|
+
|
|
698
|
+
oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
static void
|
|
704
|
+
dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
|
|
705
|
+
oj_dump_obj_to_s(obj, out);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
static void
|
|
709
|
+
dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
|
|
710
|
+
oj_dump_obj_to_s(obj, out);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
static void
|
|
714
|
+
dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
|
|
715
|
+
oj_dump_obj_to_s(obj, out);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
static DumpFunc custom_funcs[] = {
|
|
719
|
+
NULL, // RUBY_T_NONE = 0x00,
|
|
720
|
+
dump_obj, // RUBY_T_OBJECT = 0x01,
|
|
721
|
+
oj_dump_class, // RUBY_T_CLASS = 0x02,
|
|
722
|
+
oj_dump_class, // RUBY_T_MODULE = 0x03,
|
|
723
|
+
oj_dump_float, // RUBY_T_FLOAT = 0x04,
|
|
724
|
+
oj_dump_str, // RUBY_T_STRING = 0x05,
|
|
725
|
+
dump_regexp, // RUBY_T_REGEXP = 0x06,
|
|
726
|
+
dump_array, // RUBY_T_ARRAY = 0x07,
|
|
727
|
+
dump_hash, // RUBY_T_HASH = 0x08,
|
|
728
|
+
dump_struct, // RUBY_T_STRUCT = 0x09,
|
|
729
|
+
oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
|
|
730
|
+
NULL, // RUBY_T_FILE = 0x0b,
|
|
731
|
+
dump_data, // RUBY_T_DATA = 0x0c,
|
|
732
|
+
NULL, // RUBY_T_MATCH = 0x0d,
|
|
733
|
+
dump_complex, // RUBY_T_COMPLEX = 0x0e,
|
|
734
|
+
dump_rational, // RUBY_T_RATIONAL = 0x0f,
|
|
735
|
+
NULL, // 0x10
|
|
736
|
+
oj_dump_nil, // RUBY_T_NIL = 0x11,
|
|
737
|
+
oj_dump_true, // RUBY_T_TRUE = 0x12,
|
|
738
|
+
oj_dump_false, // RUBY_T_FALSE = 0x13,
|
|
739
|
+
oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
|
|
740
|
+
oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
void
|
|
744
|
+
oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
745
|
+
int type = rb_type(obj);
|
|
746
|
+
|
|
747
|
+
if (MAX_DEPTH < depth) {
|
|
748
|
+
rb_raise(rb_eNoMemError, "Too deeply nested.\n");
|
|
749
|
+
}
|
|
750
|
+
if (0 < type && type <= RUBY_T_FIXNUM) {
|
|
751
|
+
DumpFunc f = custom_funcs[type];
|
|
752
|
+
|
|
753
|
+
if (NULL != f) {
|
|
754
|
+
f(obj, depth, out, true);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
oj_dump_nil(Qnil, depth, out, false);
|
|
759
|
+
}
|