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/scp.c
CHANGED
@@ -1,31 +1,6 @@
|
|
1
1
|
/* scp.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 <stdlib.h>
|
@@ -187,7 +162,9 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
|
|
187
162
|
struct _ParseInfo pi;
|
188
163
|
VALUE input = argv[1];
|
189
164
|
|
165
|
+
parse_info_init(&pi);
|
190
166
|
pi.err_class = Qnil;
|
167
|
+
pi.max_depth = 0;
|
191
168
|
pi.options = oj_default_options;
|
192
169
|
if (3 == argc) {
|
193
170
|
oj_parse_options(argv[2], &pi.options);
|
@@ -237,6 +214,7 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
|
|
237
214
|
pi.add_value = noop_add_value;
|
238
215
|
pi.expect_value = 0;
|
239
216
|
}
|
217
|
+
pi.has_callbacks = true;
|
240
218
|
|
241
219
|
if (T_STRING == rb_type(input)) {
|
242
220
|
return oj_pi_parse(argc - 1, argv + 1, &pi, 0, 0, 1);
|
data/ext/oj/sparse.c
CHANGED
@@ -35,6 +35,7 @@
|
|
35
35
|
#include <math.h>
|
36
36
|
|
37
37
|
#include "oj.h"
|
38
|
+
#include "encode.h"
|
38
39
|
#include "parse.h"
|
39
40
|
#include "buf.h"
|
40
41
|
#include "hash.h" // for oj_strndup()
|
@@ -252,6 +253,17 @@ read_escaped_str(ParseInfo pi) {
|
|
252
253
|
case '"': buf_append(&buf, '"'); break;
|
253
254
|
case '/': buf_append(&buf, '/'); break;
|
254
255
|
case '\\': buf_append(&buf, '\\'); break;
|
256
|
+
case '\'':
|
257
|
+
// The json gem claims this is not an error despite the
|
258
|
+
// ECMA-404 indicating it is not valid.
|
259
|
+
if (CompatMode == pi->options.mode) {
|
260
|
+
buf_append(&buf, '\'');
|
261
|
+
} else {
|
262
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
263
|
+
buf_cleanup(&buf);
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
break;
|
255
267
|
case 'u':
|
256
268
|
if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
|
257
269
|
buf_cleanup(&buf);
|
@@ -428,15 +440,22 @@ read_num(ParseInfo pi) {
|
|
428
440
|
c = reader_get(&pi->rd);
|
429
441
|
}
|
430
442
|
if ('I' == c) {
|
431
|
-
if (
|
443
|
+
if (No == pi->options.allow_nan) {
|
444
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
445
|
+
return;
|
446
|
+
} else if (0 != reader_expect(&pi->rd, "nfinity")) {
|
432
447
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
433
448
|
return;
|
434
449
|
}
|
435
450
|
ni.infinity = 1;
|
436
451
|
} else {
|
437
452
|
int dec_cnt = 0;
|
453
|
+
bool zero1 = false;
|
438
454
|
|
439
455
|
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
456
|
+
if (0 == ni.i && '0' == c) {
|
457
|
+
zero1 = true;
|
458
|
+
}
|
440
459
|
if (0 < ni.i) {
|
441
460
|
dec_cnt++;
|
442
461
|
}
|
@@ -445,6 +464,13 @@ read_num(ParseInfo pi) {
|
|
445
464
|
} else {
|
446
465
|
int d = (c - '0');
|
447
466
|
|
467
|
+
if (0 < d) {
|
468
|
+
if (zero1 && CompatMode == pi->options.mode) {
|
469
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
470
|
+
return;
|
471
|
+
}
|
472
|
+
zero1 = false;
|
473
|
+
}
|
448
474
|
ni.i = ni.i * 10 + d;
|
449
475
|
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
450
476
|
ni.big = 1;
|
@@ -453,6 +479,9 @@ read_num(ParseInfo pi) {
|
|
453
479
|
}
|
454
480
|
if ('.' == c) {
|
455
481
|
c = reader_get(&pi->rd);
|
482
|
+
if (c < '0' || '9' < c) {
|
483
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
484
|
+
}
|
456
485
|
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
457
486
|
int d = (c - '0');
|
458
487
|
|
@@ -616,9 +645,17 @@ void
|
|
616
645
|
oj_sparse2(ParseInfo pi) {
|
617
646
|
int first = 1;
|
618
647
|
char c;
|
648
|
+
long start = 0;
|
619
649
|
|
620
650
|
err_init(&pi->err);
|
621
651
|
while (1) {
|
652
|
+
if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
|
653
|
+
VALUE err_clas = oj_get_json_err_class("NestingError");
|
654
|
+
|
655
|
+
oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
|
656
|
+
pi->err_class = err_clas;
|
657
|
+
return;
|
658
|
+
}
|
622
659
|
c = reader_next_non_white(&pi->rd);
|
623
660
|
if (!first && '\0' != c) {
|
624
661
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
|
@@ -657,12 +694,25 @@ oj_sparse2(ParseInfo pi) {
|
|
657
694
|
case '7':
|
658
695
|
case '8':
|
659
696
|
case '9':
|
660
|
-
case 'I':
|
661
697
|
reader_backup(&pi->rd);
|
662
698
|
read_num(pi);
|
663
699
|
break;
|
700
|
+
case 'I':
|
701
|
+
if (Yes == pi->options.allow_nan) {
|
702
|
+
reader_backup(&pi->rd);
|
703
|
+
read_num(pi);
|
704
|
+
} else {
|
705
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
706
|
+
return;
|
707
|
+
}
|
708
|
+
break;
|
664
709
|
case 'N':
|
665
|
-
|
710
|
+
if (Yes == pi->options.allow_nan) {
|
711
|
+
read_nan(pi);
|
712
|
+
} else {
|
713
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
714
|
+
return;
|
715
|
+
}
|
666
716
|
break;
|
667
717
|
case 't':
|
668
718
|
read_true(pi);
|
@@ -719,23 +769,28 @@ oj_sparse2(ParseInfo pi) {
|
|
719
769
|
}
|
720
770
|
if (stack_empty(&pi->stack)) {
|
721
771
|
if (Qundef != pi->proc) {
|
772
|
+
VALUE args[3];
|
773
|
+
long len = pi->rd.pos - start;
|
774
|
+
|
775
|
+
*args = stack_head_val(&pi->stack);
|
776
|
+
args[1] = LONG2NUM(start);
|
777
|
+
args[2] = LONG2NUM(len);
|
778
|
+
|
722
779
|
if (Qnil == pi->proc) {
|
723
|
-
|
780
|
+
rb_yield_values2(3, args);
|
724
781
|
} else {
|
725
782
|
#if HAS_PROC_WITH_BLOCK
|
726
|
-
|
727
|
-
|
728
|
-
*args = stack_head_val(&pi->stack);
|
729
|
-
rb_proc_call_with_block(pi->proc, 1, args, Qnil);
|
783
|
+
rb_proc_call_with_block(pi->proc, 3, args, Qnil);
|
730
784
|
#else
|
731
785
|
oj_set_error_at(pi, rb_eNotImpError, __FILE__, __LINE__,
|
732
786
|
"Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
|
733
787
|
return;
|
734
788
|
#endif
|
735
789
|
}
|
736
|
-
} else {
|
790
|
+
} else if (!pi->has_callbacks) {
|
737
791
|
first = 0;
|
738
792
|
}
|
793
|
+
start = pi->rd.pos;
|
739
794
|
}
|
740
795
|
}
|
741
796
|
}
|
@@ -758,11 +813,19 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
|
758
813
|
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
759
814
|
}
|
760
815
|
input = argv[0];
|
761
|
-
if (2
|
762
|
-
|
816
|
+
if (2 <= argc) {
|
817
|
+
if (T_HASH == rb_type(argv[1])) {
|
818
|
+
oj_parse_options(argv[1], &pi->options);
|
819
|
+
} else if (3 <= argc && T_HASH == rb_type(argv[2])) {
|
820
|
+
oj_parse_options(argv[2], &pi->options);
|
821
|
+
}
|
763
822
|
}
|
764
|
-
if (Qnil == input
|
765
|
-
|
823
|
+
if (Qnil == input) {
|
824
|
+
if (Yes == pi->options.nilnil) {
|
825
|
+
return Qnil;
|
826
|
+
} else {
|
827
|
+
rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
|
828
|
+
}
|
766
829
|
}
|
767
830
|
if (rb_block_given_p()) {
|
768
831
|
pi->proc = Qnil;
|
@@ -829,6 +892,19 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
|
829
892
|
if (Qnil != pi->err_class) {
|
830
893
|
pi->err.clas = pi->err_class;
|
831
894
|
}
|
895
|
+
if (CompatMode == pi->options.mode) {
|
896
|
+
// The json gem requires the error message be UTF-8 encoded. In
|
897
|
+
// additional the complete JSON source should be returned but that
|
898
|
+
// is not possible without stored all the bytes read and reading
|
899
|
+
// the remaining bytes on the stream. Both seem like a very bad
|
900
|
+
// idea.
|
901
|
+
VALUE args[] = { oj_encode(rb_str_new2(pi->err.msg)) };
|
902
|
+
|
903
|
+
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
904
|
+
} else {
|
905
|
+
oj_err_raise(&pi->err);
|
906
|
+
}
|
907
|
+
|
832
908
|
oj_err_raise(&pi->err);
|
833
909
|
}
|
834
910
|
return result;
|
@@ -0,0 +1,301 @@
|
|
1
|
+
/* stream_writer.c
|
2
|
+
* Copyright (c) 2012, 2017, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include <errno.h>
|
7
|
+
|
8
|
+
#include <ruby.h>
|
9
|
+
|
10
|
+
#include "oj.h"
|
11
|
+
|
12
|
+
extern VALUE Oj;
|
13
|
+
|
14
|
+
static void
|
15
|
+
stream_writer_free(void *ptr) {
|
16
|
+
StreamWriter sw;
|
17
|
+
|
18
|
+
if (0 == ptr) {
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
sw = (StreamWriter)ptr;
|
22
|
+
xfree(sw->sw.out.buf);
|
23
|
+
xfree(sw->sw.types);
|
24
|
+
xfree(ptr);
|
25
|
+
}
|
26
|
+
|
27
|
+
static void
|
28
|
+
stream_writer_write(StreamWriter sw) {
|
29
|
+
ssize_t size = sw->sw.out.cur - sw->sw.out.buf;
|
30
|
+
|
31
|
+
switch (sw->type) {
|
32
|
+
case STRING_IO:
|
33
|
+
rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
|
34
|
+
break;
|
35
|
+
case STREAM_IO:
|
36
|
+
rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
|
37
|
+
break;
|
38
|
+
case FILE_IO:
|
39
|
+
if (size != write(sw->fd, sw->sw.out.buf, size)) {
|
40
|
+
rb_raise(rb_eIOError, "Write failed. [_%d_:%s]\n", errno, strerror(errno));
|
41
|
+
}
|
42
|
+
break;
|
43
|
+
default:
|
44
|
+
rb_raise(rb_eArgError, "expected an IO Object.");
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
static void
|
49
|
+
stream_writer_reset_buf(StreamWriter sw) {
|
50
|
+
sw->sw.out.cur = sw->sw.out.buf;
|
51
|
+
*sw->sw.out.cur = '\0';
|
52
|
+
}
|
53
|
+
|
54
|
+
/* Document-method: new
|
55
|
+
* call-seq: new(io, options)
|
56
|
+
*
|
57
|
+
* Creates a new StreamWriter.
|
58
|
+
* - *io* [_IO_] stream to write to
|
59
|
+
* - *options* [_Hash_] formating options
|
60
|
+
*/
|
61
|
+
static VALUE
|
62
|
+
stream_writer_new(int argc, VALUE *argv, VALUE self) {
|
63
|
+
StreamWriterType type = STREAM_IO;
|
64
|
+
int fd = 0;
|
65
|
+
VALUE stream = argv[0];
|
66
|
+
VALUE clas = rb_obj_class(stream);
|
67
|
+
StreamWriter sw;
|
68
|
+
#if !IS_WINDOWS
|
69
|
+
VALUE s;
|
70
|
+
#endif
|
71
|
+
|
72
|
+
if (oj_stringio_class == clas) {
|
73
|
+
type = STRING_IO;
|
74
|
+
#if !IS_WINDOWS
|
75
|
+
} else if (rb_respond_to(stream, oj_fileno_id) &&
|
76
|
+
Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
|
77
|
+
0 != (fd = FIX2INT(s))) {
|
78
|
+
type = FILE_IO;
|
79
|
+
#endif
|
80
|
+
} else if (rb_respond_to(stream, oj_write_id)) {
|
81
|
+
type = STREAM_IO;
|
82
|
+
} else {
|
83
|
+
rb_raise(rb_eArgError, "expected an IO Object.");
|
84
|
+
}
|
85
|
+
sw = ALLOC(struct _StreamWriter);
|
86
|
+
oj_str_writer_init(&sw->sw);
|
87
|
+
if (2 == argc) {
|
88
|
+
oj_parse_options(argv[1], &sw->sw.opts);
|
89
|
+
}
|
90
|
+
sw->sw.out.indent = sw->sw.opts.indent;
|
91
|
+
sw->stream = stream;
|
92
|
+
sw->type = type;
|
93
|
+
sw->fd = fd;
|
94
|
+
|
95
|
+
return Data_Wrap_Struct(oj_stream_writer_class, 0, stream_writer_free, sw);
|
96
|
+
}
|
97
|
+
|
98
|
+
/* Document-method: push_key
|
99
|
+
* call-seq: push_key(key)
|
100
|
+
*
|
101
|
+
* Pushes a key onto the JSON document. The key will be used for the next push
|
102
|
+
* if currently in a JSON object and ignored otherwise. If a key is provided on
|
103
|
+
* the next push then that new key will be ignored.
|
104
|
+
* - *key* [_String_] the key pending for the next push
|
105
|
+
*/
|
106
|
+
static VALUE
|
107
|
+
stream_writer_push_key(VALUE self, VALUE key) {
|
108
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
109
|
+
|
110
|
+
rb_check_type(key, T_STRING);
|
111
|
+
stream_writer_reset_buf(sw);
|
112
|
+
oj_str_writer_push_key(&sw->sw, StringValuePtr(key));
|
113
|
+
stream_writer_write(sw);
|
114
|
+
return Qnil;
|
115
|
+
}
|
116
|
+
|
117
|
+
/* Document-method: push_object
|
118
|
+
* call-seq: push_object(key=nil)
|
119
|
+
*
|
120
|
+
* Pushes an object onto the JSON document. Future pushes will be to this object
|
121
|
+
* until a pop() is called.
|
122
|
+
* - *key* [_String_] the key if adding to an object in the JSON document
|
123
|
+
*/
|
124
|
+
static VALUE
|
125
|
+
stream_writer_push_object(int argc, VALUE *argv, VALUE self) {
|
126
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
127
|
+
|
128
|
+
stream_writer_reset_buf(sw);
|
129
|
+
switch (argc) {
|
130
|
+
case 0:
|
131
|
+
oj_str_writer_push_object(&sw->sw, 0);
|
132
|
+
break;
|
133
|
+
case 1:
|
134
|
+
if (Qnil == argv[0]) {
|
135
|
+
oj_str_writer_push_object(&sw->sw, 0);
|
136
|
+
} else {
|
137
|
+
rb_check_type(argv[0], T_STRING);
|
138
|
+
oj_str_writer_push_object(&sw->sw, StringValuePtr(argv[0]));
|
139
|
+
}
|
140
|
+
break;
|
141
|
+
default:
|
142
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
|
143
|
+
break;
|
144
|
+
}
|
145
|
+
stream_writer_write(sw);
|
146
|
+
return Qnil;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* Document-method: push_array
|
150
|
+
* call-seq: push_array(key=nil)
|
151
|
+
*
|
152
|
+
* Pushes an array onto the JSON document. Future pushes will be to this object
|
153
|
+
* until a pop() is called.
|
154
|
+
* - *key* [_String_] the key if adding to an object in the JSON document
|
155
|
+
*/
|
156
|
+
static VALUE
|
157
|
+
stream_writer_push_array(int argc, VALUE *argv, VALUE self) {
|
158
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
159
|
+
|
160
|
+
stream_writer_reset_buf(sw);
|
161
|
+
switch (argc) {
|
162
|
+
case 0:
|
163
|
+
oj_str_writer_push_array(&sw->sw, 0);
|
164
|
+
break;
|
165
|
+
case 1:
|
166
|
+
if (Qnil == argv[0]) {
|
167
|
+
oj_str_writer_push_array(&sw->sw, 0);
|
168
|
+
} else {
|
169
|
+
rb_check_type(argv[0], T_STRING);
|
170
|
+
oj_str_writer_push_array(&sw->sw, StringValuePtr(argv[0]));
|
171
|
+
}
|
172
|
+
break;
|
173
|
+
default:
|
174
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
|
175
|
+
break;
|
176
|
+
}
|
177
|
+
stream_writer_write(sw);
|
178
|
+
return Qnil;
|
179
|
+
}
|
180
|
+
|
181
|
+
/* Document-method: push_value
|
182
|
+
* call-seq: push_value(value, key=nil)
|
183
|
+
*
|
184
|
+
* Pushes a value onto the JSON document.
|
185
|
+
* - *value* [_Object_] value to add to the JSON document
|
186
|
+
* - *key* [_String_] the key if adding to an object in the JSON document
|
187
|
+
*/
|
188
|
+
static VALUE
|
189
|
+
stream_writer_push_value(int argc, VALUE *argv, VALUE self) {
|
190
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
191
|
+
|
192
|
+
stream_writer_reset_buf(sw);
|
193
|
+
switch (argc) {
|
194
|
+
case 1:
|
195
|
+
oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
|
196
|
+
break;
|
197
|
+
case 2:
|
198
|
+
if (Qnil == argv[1]) {
|
199
|
+
oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
|
200
|
+
} else {
|
201
|
+
rb_check_type(argv[1], T_STRING);
|
202
|
+
oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
|
203
|
+
}
|
204
|
+
break;
|
205
|
+
default:
|
206
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'.");
|
207
|
+
break;
|
208
|
+
}
|
209
|
+
stream_writer_write(sw);
|
210
|
+
return Qnil;
|
211
|
+
}
|
212
|
+
|
213
|
+
/* Document-method: push_json
|
214
|
+
* call-seq: push_json(value, key=nil)
|
215
|
+
*
|
216
|
+
* Pushes a string onto the JSON document. The String must be a valid JSON
|
217
|
+
* encoded string. No additional checking is done to verify the validity of the
|
218
|
+
* string.
|
219
|
+
* - *value* [_Object_] value to add to the JSON document
|
220
|
+
* - *key* [_String_] the key if adding to an object in the JSON document
|
221
|
+
*/
|
222
|
+
static VALUE
|
223
|
+
stream_writer_push_json(int argc, VALUE *argv, VALUE self) {
|
224
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
225
|
+
|
226
|
+
rb_check_type(argv[0], T_STRING);
|
227
|
+
stream_writer_reset_buf(sw);
|
228
|
+
switch (argc) {
|
229
|
+
case 1:
|
230
|
+
oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
|
231
|
+
break;
|
232
|
+
case 2:
|
233
|
+
if (Qnil == argv[0]) {
|
234
|
+
oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
|
235
|
+
} else {
|
236
|
+
rb_check_type(argv[1], T_STRING);
|
237
|
+
oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), StringValuePtr(argv[1]));
|
238
|
+
}
|
239
|
+
break;
|
240
|
+
default:
|
241
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_json'.");
|
242
|
+
break;
|
243
|
+
}
|
244
|
+
stream_writer_write(sw);
|
245
|
+
return Qnil;
|
246
|
+
}
|
247
|
+
|
248
|
+
/* Document-method: pop
|
249
|
+
* call-seq: pop()
|
250
|
+
*
|
251
|
+
* Pops up a level in the JSON document closing the array or object that is
|
252
|
+
* currently open.
|
253
|
+
*/
|
254
|
+
static VALUE
|
255
|
+
stream_writer_pop(VALUE self) {
|
256
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
257
|
+
|
258
|
+
stream_writer_reset_buf(sw);
|
259
|
+
oj_str_writer_pop(&sw->sw);
|
260
|
+
stream_writer_write(sw);
|
261
|
+
return Qnil;
|
262
|
+
}
|
263
|
+
|
264
|
+
/* Document-method: pop_all
|
265
|
+
* call-seq: pop_all()
|
266
|
+
*
|
267
|
+
* Pops all level in the JSON document closing all the array or object that is
|
268
|
+
* currently open.
|
269
|
+
*/
|
270
|
+
static VALUE
|
271
|
+
stream_writer_pop_all(VALUE self) {
|
272
|
+
StreamWriter sw = (StreamWriter)DATA_PTR(self);
|
273
|
+
|
274
|
+
stream_writer_reset_buf(sw);
|
275
|
+
oj_str_writer_pop_all(&sw->sw);
|
276
|
+
stream_writer_write(sw);
|
277
|
+
|
278
|
+
return Qnil;
|
279
|
+
}
|
280
|
+
|
281
|
+
/* Document-class: Oj::StreamWriter
|
282
|
+
*
|
283
|
+
* Supports building a JSON document one element at a time. Build the IO stream
|
284
|
+
* document by pushing values into the document. Pushing an array or an object
|
285
|
+
* will create that element in the JSON document and subsequent pushes will add
|
286
|
+
* the elements to that array or object until a pop() is called.
|
287
|
+
*/
|
288
|
+
void
|
289
|
+
oj_stream_writer_init() {
|
290
|
+
oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
|
291
|
+
rb_define_module_function(oj_stream_writer_class, "new", stream_writer_new, -1);
|
292
|
+
rb_define_method(oj_stream_writer_class, "push_key", stream_writer_push_key, 1);
|
293
|
+
rb_define_method(oj_stream_writer_class, "push_object", stream_writer_push_object, -1);
|
294
|
+
rb_define_method(oj_stream_writer_class, "push_array", stream_writer_push_array, -1);
|
295
|
+
rb_define_method(oj_stream_writer_class, "push_value", stream_writer_push_value, -1);
|
296
|
+
rb_define_method(oj_stream_writer_class, "push_json", stream_writer_push_json, -1);
|
297
|
+
rb_define_method(oj_stream_writer_class, "pop", stream_writer_pop, 0);
|
298
|
+
rb_define_method(oj_stream_writer_class, "pop_all", stream_writer_pop_all, 0);
|
299
|
+
}
|
300
|
+
|
301
|
+
|