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/err.c
CHANGED
@@ -1,31 +1,6 @@
|
|
1
1
|
/* err.c
|
2
2
|
* Copyright (c) 2011, 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 <stdarg.h>
|
data/ext/oj/err.h
CHANGED
@@ -32,6 +32,10 @@
|
|
32
32
|
#define __OJ_ERR_H__
|
33
33
|
|
34
34
|
#include "ruby.h"
|
35
|
+
// Needed to silence 2.4.0 warnings.
|
36
|
+
#ifndef NORETURN
|
37
|
+
# define NORETURN(x) x
|
38
|
+
#endif
|
35
39
|
|
36
40
|
#define set_error(err, eclas, msg, json, current) _oj_err_set_with_location(err, eclas, msg, json, current, __FILE__, __LINE__)
|
37
41
|
|
@@ -44,10 +48,12 @@ extern VALUE oj_parse_error_class;
|
|
44
48
|
|
45
49
|
extern void oj_err_set(Err e, VALUE clas, const char *format, ...);
|
46
50
|
extern void _oj_err_set_with_location(Err err, VALUE eclas, const char *msg, const char *json, const char *current, const char* file, int line);
|
47
|
-
|
51
|
+
|
52
|
+
NORETURN(extern void oj_err_raise(Err e));
|
48
53
|
|
49
54
|
#define raise_error(msg, json, current) _oj_raise_error(msg, json, current, __FILE__, __LINE__)
|
50
|
-
|
55
|
+
|
56
|
+
NORETURN(extern void _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line));
|
51
57
|
|
52
58
|
|
53
59
|
inline static void
|
data/ext/oj/fast.c
CHANGED
@@ -1099,14 +1099,14 @@ each_value(Doc doc, Leaf leaf) {
|
|
1099
1099
|
|
1100
1100
|
// doc functions
|
1101
1101
|
|
1102
|
-
/*
|
1102
|
+
/* @overload open(json) { |doc| ... } => Object
|
1103
1103
|
*
|
1104
1104
|
* Parses a JSON document String and then yields to the provided block if one
|
1105
1105
|
* is given with an instance of the Oj::Doc as the single yield parameter. If
|
1106
1106
|
* a block is not given then an Oj::Doc instance is returned and must be
|
1107
1107
|
* closed with a call to the #close() method when no longer needed.
|
1108
1108
|
*
|
1109
|
-
*
|
1109
|
+
* @param [String] json JSON document string
|
1110
1110
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1111
1111
|
* @yieldreturn [Object] returns the result of the yield as the result of the method call
|
1112
1112
|
* @example
|
@@ -1140,14 +1140,14 @@ doc_open(VALUE clas, VALUE str) {
|
|
1140
1140
|
return obj;
|
1141
1141
|
}
|
1142
1142
|
|
1143
|
-
/*
|
1143
|
+
/* @overload open_file(filename) { |doc| ... } => Object
|
1144
1144
|
*
|
1145
1145
|
* Parses a JSON document from a file and then yields to the provided block if
|
1146
1146
|
* one is given with an instance of the Oj::Doc as the single yield
|
1147
1147
|
* parameter. If a block is not given then an Oj::Doc instance is returned and
|
1148
1148
|
* must be closed with a call to the #close() method when no longer needed.
|
1149
1149
|
*
|
1150
|
-
*
|
1150
|
+
* @param [String] filename name of file that contains a JSON document
|
1151
1151
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1152
1152
|
* @yieldreturn [Object] returns the result of the yield as the result of the method call
|
1153
1153
|
* @example
|
@@ -1223,7 +1223,7 @@ append_key(char *p, const char *key) {
|
|
1223
1223
|
* @see Oj::Doc.open
|
1224
1224
|
*/
|
1225
1225
|
|
1226
|
-
/*
|
1226
|
+
/* @overload where?() => String
|
1227
1227
|
*
|
1228
1228
|
* Returns a String that describes the absolute path to the current location
|
1229
1229
|
* in the JSON document.
|
@@ -1265,7 +1265,7 @@ doc_where(VALUE self) {
|
|
1265
1265
|
}
|
1266
1266
|
}
|
1267
1267
|
|
1268
|
-
/*
|
1268
|
+
/* @overload local_key() => String, Fixnum, nil
|
1269
1269
|
*
|
1270
1270
|
* Returns the final key to the current location.
|
1271
1271
|
* @example
|
@@ -1288,7 +1288,7 @@ doc_local_key(VALUE self) {
|
|
1288
1288
|
return key;
|
1289
1289
|
}
|
1290
1290
|
|
1291
|
-
/*
|
1291
|
+
/* @overload home() => nil
|
1292
1292
|
*
|
1293
1293
|
* Moves the document marker or location to the hoot or home position. The
|
1294
1294
|
* same operation can be performed with a Oj::Doc.move('/').
|
@@ -1305,13 +1305,13 @@ doc_home(VALUE self) {
|
|
1305
1305
|
return oj_slash_string;
|
1306
1306
|
}
|
1307
1307
|
|
1308
|
-
/*
|
1308
|
+
/* @overload type(path=nil) => Class
|
1309
1309
|
*
|
1310
1310
|
* Returns the Class of the data value at the location identified by the path
|
1311
1311
|
* or the current location if the path is nil or not provided. This method
|
1312
1312
|
* does not create the Ruby Object at the location specified so the overhead
|
1313
1313
|
* is low.
|
1314
|
-
*
|
1314
|
+
* @param [String] path path to the location to get the type of if provided
|
1315
1315
|
* @example
|
1316
1316
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
1317
1317
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
@@ -1347,14 +1347,14 @@ doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1347
1347
|
return type;
|
1348
1348
|
}
|
1349
1349
|
|
1350
|
-
/*
|
1350
|
+
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
|
1351
1351
|
*
|
1352
1352
|
* Returns the value at the location identified by the path or the current
|
1353
1353
|
* location if the path is nil or not provided. This method will create and
|
1354
1354
|
* return an Array or Hash if that is the type of Object at the location
|
1355
1355
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1356
1356
|
* document.
|
1357
|
-
*
|
1357
|
+
* @param [String] path path to the location to get the type of if provided
|
1358
1358
|
* @example
|
1359
1359
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
1360
1360
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
@@ -1380,12 +1380,12 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1380
1380
|
return val;
|
1381
1381
|
}
|
1382
1382
|
|
1383
|
-
/*
|
1383
|
+
/* @overload each_leaf(path=nil) => nil
|
1384
1384
|
*
|
1385
1385
|
* Yields to the provided block for each leaf node with the identified
|
1386
1386
|
* location of the JSON document as the root. The parameter passed to the
|
1387
1387
|
* block on yield is the Doc instance after moving to the child location.
|
1388
|
-
*
|
1388
|
+
* @param [String] path if provided it identified the top of the branch to process the leaves of
|
1389
1389
|
* @yieldparam [Doc] Doc at the child location
|
1390
1390
|
* @example
|
1391
1391
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1429,11 +1429,11 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1429
1429
|
return Qnil;
|
1430
1430
|
}
|
1431
1431
|
|
1432
|
-
/*
|
1432
|
+
/* @overload move(path) => nil
|
1433
1433
|
*
|
1434
1434
|
* Moves the document marker to the path specified. The path can an absolute
|
1435
1435
|
* path or a relative path.
|
1436
|
-
*
|
1436
|
+
* @param [String] path path to the location to move to
|
1437
1437
|
* @example
|
1438
1438
|
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
|
1439
1439
|
*/
|
@@ -1455,13 +1455,13 @@ doc_move(VALUE self, VALUE str) {
|
|
1455
1455
|
return Qnil;
|
1456
1456
|
}
|
1457
1457
|
|
1458
|
-
/*
|
1458
|
+
/* @overload each_child(path=nil) { |doc| ... } => nil
|
1459
1459
|
*
|
1460
1460
|
* Yields to the provided block for each immediate child node with the
|
1461
1461
|
* identified location of the JSON document as the root. The parameter passed
|
1462
1462
|
* to the block on yield is the Doc instance after moving to the child
|
1463
1463
|
* location.
|
1464
|
-
*
|
1464
|
+
* @param [String] path if provided it identified the top of the branch to process the chilren of
|
1465
1465
|
* @yieldparam [Doc] Doc at the child location
|
1466
1466
|
* @example
|
1467
1467
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1515,13 +1515,13 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1515
1515
|
return Qnil;
|
1516
1516
|
}
|
1517
1517
|
|
1518
|
-
/*
|
1518
|
+
/* @overload each_value(path=nil) { |val| ... } => nil
|
1519
1519
|
*
|
1520
1520
|
* Yields to the provided block for each leaf value in the identified location
|
1521
1521
|
* of the JSON document. The parameter passed to the block on yield is the
|
1522
1522
|
* value of the leaf. Only those leaves below the element specified by the
|
1523
1523
|
* path parameter are processed.
|
1524
|
-
*
|
1524
|
+
* @param [String] path if provided it identified the top of the branch to process the leaf values of
|
1525
1525
|
* @yieldparam [Object] val each leaf value
|
1526
1526
|
* @example
|
1527
1527
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1556,12 +1556,12 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1556
1556
|
return Qnil;
|
1557
1557
|
}
|
1558
1558
|
|
1559
|
-
/*
|
1559
|
+
/* @overload dump(path, filename)
|
1560
1560
|
*
|
1561
1561
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
1562
1562
|
* options for generating the JSON.
|
1563
|
-
*
|
1564
|
-
*
|
1563
|
+
* @param path [String] if provided it identified the top of the branch to dump to JSON
|
1564
|
+
* @param filename [String] if provided it is the filename to write the output to
|
1565
1565
|
* @example
|
1566
1566
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
1567
1567
|
* doc.dump('/2')
|
@@ -1610,7 +1610,7 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1610
1610
|
return Qnil;
|
1611
1611
|
}
|
1612
1612
|
|
1613
|
-
/*
|
1613
|
+
/* @overload size() => Fixnum
|
1614
1614
|
*
|
1615
1615
|
* Returns the number of nodes in the JSON document where a node is any one of
|
1616
1616
|
* the basic JSON components.
|
@@ -1623,7 +1623,7 @@ doc_size(VALUE self) {
|
|
1623
1623
|
return ULONG2NUM(((Doc)DATA_PTR(self))->size);
|
1624
1624
|
}
|
1625
1625
|
|
1626
|
-
/*
|
1626
|
+
/* @overload close() => nil
|
1627
1627
|
*
|
1628
1628
|
* Closes an open document. No further calls to the document will be valid
|
1629
1629
|
* after closing.
|
data/ext/oj/mimic_json.c
ADDED
@@ -0,0 +1,817 @@
|
|
1
|
+
/* mimic_json.c
|
2
|
+
* Copyright (c) 2012, 2017, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "oj.h"
|
7
|
+
#include "encode.h"
|
8
|
+
#include "dump.h"
|
9
|
+
#include "parse.h"
|
10
|
+
|
11
|
+
static VALUE symbolize_names_sym;
|
12
|
+
|
13
|
+
static const char json_class[] = "json_class";
|
14
|
+
|
15
|
+
VALUE oj_array_nl_sym;
|
16
|
+
VALUE oj_ascii_only_sym;
|
17
|
+
VALUE oj_json_generator_error_class;
|
18
|
+
VALUE oj_json_parser_error_class;
|
19
|
+
VALUE oj_max_nesting_sym;
|
20
|
+
VALUE oj_object_nl_sym;
|
21
|
+
VALUE oj_space_before_sym;
|
22
|
+
VALUE oj_space_sym;
|
23
|
+
|
24
|
+
static VALUE state_class;
|
25
|
+
|
26
|
+
// mimic JSON documentation
|
27
|
+
|
28
|
+
/* Document-module: JSON::Ext
|
29
|
+
*
|
30
|
+
* The Ext module is a placeholder in the mimic JSON module used for
|
31
|
+
* compatibility only.
|
32
|
+
*/
|
33
|
+
/* Document-class: JSON::Ext::Parser
|
34
|
+
*
|
35
|
+
* The JSON::Ext::Parser is a placeholder in the mimic JSON module used for
|
36
|
+
* compatibility only.
|
37
|
+
*/
|
38
|
+
/* Document-class: JSON::Ext::Generator
|
39
|
+
*
|
40
|
+
* The JSON::Ext::Generator is a placeholder in the mimic JSON module used for
|
41
|
+
* compatibility only.
|
42
|
+
*/
|
43
|
+
|
44
|
+
/* Document-method: parser=
|
45
|
+
* call-seq: parser=(parser)
|
46
|
+
*
|
47
|
+
* Does nothing other than provide compatibiltiy.
|
48
|
+
* - *parser* [_Object_] ignored
|
49
|
+
*/
|
50
|
+
/* Document-method: generator=
|
51
|
+
* call-seq: generator=(generator)
|
52
|
+
*
|
53
|
+
* Does nothing other than provide compatibiltiy.
|
54
|
+
* - *generator* [_Object_] ignored
|
55
|
+
*/
|
56
|
+
|
57
|
+
VALUE
|
58
|
+
oj_get_json_err_class(const char *err_classname) {
|
59
|
+
volatile VALUE json_module;
|
60
|
+
volatile VALUE clas;
|
61
|
+
volatile VALUE json_error_class;
|
62
|
+
|
63
|
+
if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
|
64
|
+
json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
65
|
+
} else {
|
66
|
+
json_module = rb_define_module("JSON");
|
67
|
+
}
|
68
|
+
if (rb_const_defined_at(json_module, rb_intern("JSONError"))) {
|
69
|
+
json_error_class = rb_const_get(json_module, rb_intern("JSONError"));
|
70
|
+
} else {
|
71
|
+
json_error_class = rb_define_class_under(json_module, "JSONError", rb_eStandardError);
|
72
|
+
}
|
73
|
+
if (0 == strcmp(err_classname, "JSONError")) {
|
74
|
+
clas = json_error_class;
|
75
|
+
} else {
|
76
|
+
if (rb_const_defined_at(json_module, rb_intern(err_classname))) {
|
77
|
+
clas = rb_const_get(json_module, rb_intern(err_classname));
|
78
|
+
} else {
|
79
|
+
clas = rb_define_class_under(json_module, err_classname, json_error_class);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
return clas;
|
83
|
+
}
|
84
|
+
|
85
|
+
void
|
86
|
+
oj_parse_mimic_dump_options(VALUE ropts, Options copts) {
|
87
|
+
VALUE v;
|
88
|
+
size_t len;
|
89
|
+
|
90
|
+
if (T_HASH != rb_type(ropts)) {
|
91
|
+
if (rb_respond_to(ropts, oj_to_hash_id)) {
|
92
|
+
ropts = rb_funcall(ropts, oj_to_hash_id, 0);
|
93
|
+
} else if (rb_respond_to(ropts, oj_to_h_id)) {
|
94
|
+
ropts = rb_funcall(ropts, oj_to_h_id, 0);
|
95
|
+
} else {
|
96
|
+
rb_raise(rb_eArgError, "options must be a hash.");
|
97
|
+
}
|
98
|
+
}
|
99
|
+
v = rb_hash_lookup(ropts, oj_max_nesting_sym);
|
100
|
+
if (Qtrue == v) {
|
101
|
+
copts->dump_opts.max_depth = 100;
|
102
|
+
} else if (Qfalse == v || Qnil == v) {
|
103
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
104
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
105
|
+
copts->dump_opts.max_depth = NUM2INT(v);
|
106
|
+
if (0 >= copts->dump_opts.max_depth) {
|
107
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
|
111
|
+
copts->dump_opts.nan_dump = (Qtrue == v);
|
112
|
+
}
|
113
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_indent_sym))) {
|
114
|
+
rb_check_type(v, T_STRING);
|
115
|
+
if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
|
116
|
+
rb_raise(rb_eArgError, "indent string is limited to %lu characters.", sizeof(copts->dump_opts.indent_str));
|
117
|
+
}
|
118
|
+
strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
|
119
|
+
copts->dump_opts.indent_size = (uint8_t)len;
|
120
|
+
copts->dump_opts.use = true;
|
121
|
+
}
|
122
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_space_sym))) {
|
123
|
+
rb_check_type(v, T_STRING);
|
124
|
+
if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
|
125
|
+
rb_raise(rb_eArgError, "space string is limited to %lu characters.", sizeof(copts->dump_opts.after_sep));
|
126
|
+
}
|
127
|
+
strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
|
128
|
+
copts->dump_opts.after_size = (uint8_t)len;
|
129
|
+
copts->dump_opts.use = true;
|
130
|
+
}
|
131
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
|
132
|
+
rb_check_type(v, T_STRING);
|
133
|
+
if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
|
134
|
+
rb_raise(rb_eArgError, "space_before string is limited to %lu characters.", sizeof(copts->dump_opts.before_sep));
|
135
|
+
}
|
136
|
+
strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
|
137
|
+
copts->dump_opts.before_size = (uint8_t)len;
|
138
|
+
copts->dump_opts.use = true;
|
139
|
+
}
|
140
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
|
141
|
+
rb_check_type(v, T_STRING);
|
142
|
+
if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
|
143
|
+
rb_raise(rb_eArgError, "object_nl string is limited to %lu characters.", sizeof(copts->dump_opts.hash_nl));
|
144
|
+
}
|
145
|
+
strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
|
146
|
+
copts->dump_opts.hash_size = (uint8_t)len;
|
147
|
+
copts->dump_opts.use = true;
|
148
|
+
}
|
149
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
|
150
|
+
rb_check_type(v, T_STRING);
|
151
|
+
if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
|
152
|
+
rb_raise(rb_eArgError, "array_nl string is limited to %lu characters.", sizeof(copts->dump_opts.array_nl));
|
153
|
+
}
|
154
|
+
strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
|
155
|
+
copts->dump_opts.array_size = (uint8_t)len;
|
156
|
+
copts->dump_opts.use = true;
|
157
|
+
}
|
158
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_ascii_only_sym))) {
|
159
|
+
// generate seems to assume anything except nil and false are true.
|
160
|
+
if (Qfalse == v) {
|
161
|
+
copts->escape_mode = JXEsc; // JSONEsc;
|
162
|
+
} else {
|
163
|
+
copts->escape_mode = ASCIIEsc;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
static int
|
169
|
+
mimic_limit_arg(VALUE a) {
|
170
|
+
if (Qnil == a || T_FIXNUM != rb_type(a)) {
|
171
|
+
return -1;
|
172
|
+
}
|
173
|
+
return NUM2INT(a);
|
174
|
+
}
|
175
|
+
|
176
|
+
/* Document-method: dump
|
177
|
+
* call-seq: dump(obj, anIO=nil, limit=nil)
|
178
|
+
*
|
179
|
+
* Encodes an object as a JSON String.
|
180
|
+
*
|
181
|
+
* - *obj* [_Object_] object to convert to encode as JSON
|
182
|
+
* - *anIO* [_IO_] an IO that allows writing
|
183
|
+
* - *limit* [_Fixnum_] ignored
|
184
|
+
*
|
185
|
+
* Returns [_String_] a JSON string.
|
186
|
+
*/
|
187
|
+
static VALUE
|
188
|
+
mimic_dump(int argc, VALUE *argv, VALUE self) {
|
189
|
+
char buf[4096];
|
190
|
+
struct _Out out;
|
191
|
+
struct _Options copts = oj_default_options;
|
192
|
+
VALUE rstr;
|
193
|
+
|
194
|
+
copts.str_rx.head = NULL;
|
195
|
+
copts.str_rx.tail = NULL;
|
196
|
+
out.buf = buf;
|
197
|
+
out.end = buf + sizeof(buf) - 10;
|
198
|
+
out.allocated = 0;
|
199
|
+
out.caller = CALLER_DUMP;
|
200
|
+
|
201
|
+
if (No == copts.nilnil && Qnil == *argv) {
|
202
|
+
rb_raise(rb_eTypeError, "nil not allowed.");
|
203
|
+
}
|
204
|
+
copts.dump_opts.max_depth = MAX_DEPTH; // when using dump there is no limit
|
205
|
+
out.omit_nil = copts.dump_opts.omit_nil;
|
206
|
+
if (2 <= argc) {
|
207
|
+
int limit;
|
208
|
+
|
209
|
+
// The json gem take a more liberal approach to optional
|
210
|
+
// arguments. Expected are (obj, anIO=nil, limit=nil) yet the io
|
211
|
+
// argument can be left off completely and the 2nd argument is then
|
212
|
+
// the limit.
|
213
|
+
if (0 <= (limit = mimic_limit_arg(argv[1]))) {
|
214
|
+
copts.dump_opts.max_depth = limit;
|
215
|
+
}
|
216
|
+
if (3 <= argc && 0 <= (limit = mimic_limit_arg(argv[2]))) {
|
217
|
+
copts.dump_opts.max_depth = limit;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
oj_dump_obj_to_json(*argv, &copts, &out);
|
221
|
+
if (0 == out.buf) {
|
222
|
+
rb_raise(rb_eNoMemError, "Not enough memory.");
|
223
|
+
}
|
224
|
+
rstr = rb_str_new2(out.buf);
|
225
|
+
rstr = oj_encode(rstr);
|
226
|
+
if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
|
227
|
+
VALUE io = argv[1];
|
228
|
+
VALUE args[1];
|
229
|
+
|
230
|
+
*args = rstr;
|
231
|
+
rb_funcall2(io, oj_write_id, 1, args);
|
232
|
+
rstr = io;
|
233
|
+
}
|
234
|
+
if (out.allocated) {
|
235
|
+
xfree(out.buf);
|
236
|
+
}
|
237
|
+
return rstr;
|
238
|
+
}
|
239
|
+
|
240
|
+
// This is the signature for the hash_foreach callback also.
|
241
|
+
static int
|
242
|
+
mimic_walk(VALUE key, VALUE obj, VALUE proc) {
|
243
|
+
switch (rb_type(obj)) {
|
244
|
+
case T_HASH:
|
245
|
+
rb_hash_foreach(obj, mimic_walk, proc);
|
246
|
+
break;
|
247
|
+
case T_ARRAY:
|
248
|
+
{
|
249
|
+
size_t cnt = RARRAY_LEN(obj);
|
250
|
+
size_t i;
|
251
|
+
|
252
|
+
for (i = 0; i < cnt; i++) {
|
253
|
+
mimic_walk(Qnil, rb_ary_entry(obj, i), proc);
|
254
|
+
}
|
255
|
+
break;
|
256
|
+
}
|
257
|
+
default:
|
258
|
+
break;
|
259
|
+
}
|
260
|
+
if (Qnil == proc) {
|
261
|
+
if (rb_block_given_p()) {
|
262
|
+
rb_yield(obj);
|
263
|
+
}
|
264
|
+
} else {
|
265
|
+
#if HAS_PROC_WITH_BLOCK
|
266
|
+
VALUE args[1];
|
267
|
+
|
268
|
+
*args = obj;
|
269
|
+
rb_proc_call_with_block(proc, 1, args, Qnil);
|
270
|
+
#else
|
271
|
+
rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
|
272
|
+
#endif
|
273
|
+
}
|
274
|
+
return ST_CONTINUE;
|
275
|
+
}
|
276
|
+
|
277
|
+
/* Document-method: restore
|
278
|
+
* call-seq: restore(source, proc=nil)
|
279
|
+
*
|
280
|
+
* Loads a Ruby Object from a JSON source that can be either a String or an
|
281
|
+
* IO. If Proc is given or a block is providedit is called with each nested
|
282
|
+
* element of the loaded Object.
|
283
|
+
*
|
284
|
+
* - *source* [_String_|IO] JSON source
|
285
|
+
* - *proc* [_Proc_] to yield to on each element or nil
|
286
|
+
*
|
287
|
+
* Returns [_Object_] the decoded Object.
|
288
|
+
*/
|
289
|
+
|
290
|
+
/* Document-method: load
|
291
|
+
* call-seq: load(source, proc=nil)
|
292
|
+
*
|
293
|
+
* Loads a Ruby Object from a JSON source that can be either a String or an
|
294
|
+
* IO. If Proc is given or a block is providedit is called with each nested
|
295
|
+
* element of the loaded Object.
|
296
|
+
*
|
297
|
+
* - *source* [_String_|IO] JSON source
|
298
|
+
* - *proc* [_Proc_] to yield to on each element or nil
|
299
|
+
*
|
300
|
+
* Returns [_Object_] the decode Object.
|
301
|
+
*/
|
302
|
+
static VALUE
|
303
|
+
mimic_load(int argc, VALUE *argv, VALUE self) {
|
304
|
+
VALUE obj;
|
305
|
+
VALUE p = Qnil;
|
306
|
+
|
307
|
+
obj = oj_compat_parse(argc, argv, self);
|
308
|
+
if (2 <= argc) {
|
309
|
+
if (rb_cProc == rb_obj_class(argv[1])) {
|
310
|
+
p = argv[1];
|
311
|
+
} else if (3 <= argc) {
|
312
|
+
if (rb_cProc == rb_obj_class(argv[2])) {
|
313
|
+
p = argv[2];
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
mimic_walk(Qnil, obj, p);
|
318
|
+
|
319
|
+
return obj;
|
320
|
+
}
|
321
|
+
|
322
|
+
/* Document-method: []
|
323
|
+
* call-seq: [](obj, opts={})
|
324
|
+
*
|
325
|
+
* If the obj argument is a String then it is assumed to be a JSON String and
|
326
|
+
* parsed otherwise the obj is encoded as a JSON String.
|
327
|
+
*
|
328
|
+
* - *obj* [_String_|Hash|Array] object to convert
|
329
|
+
* - *opts* [_Hash_] same options as either generate or parse
|
330
|
+
*
|
331
|
+
* Returns [_Object_]
|
332
|
+
*/
|
333
|
+
static VALUE
|
334
|
+
mimic_dump_load(int argc, VALUE *argv, VALUE self) {
|
335
|
+
if (1 > argc) {
|
336
|
+
rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
|
337
|
+
} else if (T_STRING == rb_type(*argv)) {
|
338
|
+
return mimic_load(argc, argv, self);
|
339
|
+
} else {
|
340
|
+
return mimic_dump(argc, argv, self);
|
341
|
+
}
|
342
|
+
return Qnil;
|
343
|
+
}
|
344
|
+
|
345
|
+
static VALUE
|
346
|
+
mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
347
|
+
char buf[4096];
|
348
|
+
struct _Out out;
|
349
|
+
VALUE rstr;
|
350
|
+
|
351
|
+
out.buf = buf;
|
352
|
+
out.end = buf + sizeof(buf) - 10;
|
353
|
+
out.allocated = 0;
|
354
|
+
out.omit_nil = copts->dump_opts.omit_nil;
|
355
|
+
out.caller = CALLER_GENERATE;
|
356
|
+
// For obj.to_json or generate nan is not allowed but if called from dump
|
357
|
+
// it is.
|
358
|
+
copts->dump_opts.nan_dump = false;
|
359
|
+
copts->mode = CompatMode;
|
360
|
+
if (2 == argc && Qnil != argv[1]) {
|
361
|
+
oj_parse_mimic_dump_options(argv[1], copts);
|
362
|
+
}
|
363
|
+
if (No == copts->nilnil && Qnil == *argv) {
|
364
|
+
rb_raise(rb_eTypeError, "nil not allowed.");
|
365
|
+
}
|
366
|
+
oj_dump_obj_to_json(*argv, copts, &out);
|
367
|
+
if (0 == out.buf) {
|
368
|
+
rb_raise(rb_eNoMemError, "Not enough memory.");
|
369
|
+
}
|
370
|
+
rstr = rb_str_new2(out.buf);
|
371
|
+
rstr = oj_encode(rstr);
|
372
|
+
if (out.allocated) {
|
373
|
+
xfree(out.buf);
|
374
|
+
}
|
375
|
+
return rstr;
|
376
|
+
}
|
377
|
+
|
378
|
+
/* Document-method: fast_generate
|
379
|
+
* call-seq: fast_generate(obj, opts=nil)
|
380
|
+
* Same as generate().
|
381
|
+
* @see generate
|
382
|
+
*/
|
383
|
+
|
384
|
+
/* Document-method: generate
|
385
|
+
* call-seq: generate(obj, opts=nil)
|
386
|
+
*
|
387
|
+
* Encode obj as a JSON String. The obj argument must be a Hash, Array, or
|
388
|
+
* respond to to_h or to_json. Options other than those listed such as
|
389
|
+
* +:allow_nan+ or +:max_nesting+ are ignored.
|
390
|
+
*
|
391
|
+
* - *obj* [_Object_|Hash|Array] object to convert to a JSON String
|
392
|
+
* - *opts* [_Hash_] options
|
393
|
+
* - - *:indent* [_String_] String to use for indentation.
|
394
|
+
* - *:space* [_String_] String placed after a , or : delimiter
|
395
|
+
* - *:space_before* [_String_] String placed before a : delimiter
|
396
|
+
* - *:object_nl* [_String_] String placed after a JSON object
|
397
|
+
* - *:array_nl* [_String_] String placed after a JSON array
|
398
|
+
* - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output. Note JSON.generate does support this even if it is not documented.
|
399
|
+
*
|
400
|
+
* Returns [_String_] generated JSON.
|
401
|
+
*/
|
402
|
+
VALUE
|
403
|
+
oj_mimic_generate(int argc, VALUE *argv, VALUE self) {
|
404
|
+
struct _Options copts = oj_default_options;
|
405
|
+
|
406
|
+
copts.str_rx.head = NULL;
|
407
|
+
copts.str_rx.tail = NULL;
|
408
|
+
|
409
|
+
return mimic_generate_core(argc, argv, &copts);
|
410
|
+
}
|
411
|
+
|
412
|
+
/* Document-method: pretty_generate
|
413
|
+
* call-seq: pretty_generate(obj, opts=nil)
|
414
|
+
*
|
415
|
+
* Same as generate() but with different defaults for the spacing options.
|
416
|
+
* @see generate
|
417
|
+
*
|
418
|
+
* Return [_String_] the generated JSON.
|
419
|
+
*/
|
420
|
+
VALUE
|
421
|
+
oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
|
422
|
+
struct _Options copts = oj_default_options;
|
423
|
+
|
424
|
+
copts.str_rx.head = NULL;
|
425
|
+
copts.str_rx.tail = NULL;
|
426
|
+
strcpy(copts.dump_opts.indent_str, " ");
|
427
|
+
copts.dump_opts.indent_size = (uint8_t)strlen(copts.dump_opts.indent_str);
|
428
|
+
strcpy(copts.dump_opts.before_sep, "");
|
429
|
+
copts.dump_opts.before_size = (uint8_t)strlen(copts.dump_opts.before_sep);
|
430
|
+
strcpy(copts.dump_opts.after_sep, " ");
|
431
|
+
copts.dump_opts.after_size = (uint8_t)strlen(copts.dump_opts.after_sep);
|
432
|
+
strcpy(copts.dump_opts.hash_nl, "\n");
|
433
|
+
copts.dump_opts.hash_size = (uint8_t)strlen(copts.dump_opts.hash_nl);
|
434
|
+
strcpy(copts.dump_opts.array_nl, "\n");
|
435
|
+
copts.dump_opts.array_size = (uint8_t)strlen(copts.dump_opts.array_nl);
|
436
|
+
copts.dump_opts.use = true;
|
437
|
+
|
438
|
+
return mimic_generate_core(argc, argv, &copts);
|
439
|
+
}
|
440
|
+
|
441
|
+
static VALUE
|
442
|
+
mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
|
443
|
+
struct _ParseInfo pi;
|
444
|
+
VALUE args[1];
|
445
|
+
|
446
|
+
if (argc < 1) {
|
447
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
448
|
+
}
|
449
|
+
parse_info_init(&pi);
|
450
|
+
oj_set_compat_callbacks(&pi);
|
451
|
+
// TBD
|
452
|
+
pi.err_class = oj_json_parser_error_class;
|
453
|
+
//pi.err_class = Qnil;
|
454
|
+
|
455
|
+
pi.options = oj_default_options;
|
456
|
+
pi.options.auto_define = No;
|
457
|
+
pi.options.quirks_mode = Yes;
|
458
|
+
pi.options.allow_invalid = No;
|
459
|
+
pi.options.empty_string = No;
|
460
|
+
pi.options.create_ok = No;
|
461
|
+
pi.options.allow_nan = (bang ? Yes : No);
|
462
|
+
pi.options.nilnil = No;
|
463
|
+
pi.max_depth = 100;
|
464
|
+
|
465
|
+
if (2 <= argc) {
|
466
|
+
VALUE ropts = argv[1];
|
467
|
+
VALUE v;
|
468
|
+
|
469
|
+
if (T_HASH != rb_type(ropts)) {
|
470
|
+
rb_raise(rb_eArgError, "options must be a hash.");
|
471
|
+
}
|
472
|
+
if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
|
473
|
+
pi.options.sym_key = (Qtrue == v) ? Yes : No;
|
474
|
+
}
|
475
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
|
476
|
+
pi.options.quirks_mode = (Qtrue == v) ? Yes : No;
|
477
|
+
}
|
478
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_create_additions_sym))) {
|
479
|
+
pi.options.create_ok = (Qtrue == v) ? Yes : No;
|
480
|
+
}
|
481
|
+
if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
|
482
|
+
pi.options.allow_nan = (Qtrue == v) ? Yes : No;
|
483
|
+
}
|
484
|
+
|
485
|
+
if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
|
486
|
+
if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
|
487
|
+
pi.options.hash_class = Qnil;
|
488
|
+
} else {
|
489
|
+
rb_check_type(v, T_CLASS);
|
490
|
+
pi.options.hash_class = v;
|
491
|
+
}
|
492
|
+
}
|
493
|
+
if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
|
494
|
+
if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
|
495
|
+
pi.options.hash_class = Qnil;
|
496
|
+
} else {
|
497
|
+
rb_check_type(v, T_CLASS);
|
498
|
+
pi.options.hash_class = v;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
|
502
|
+
if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
|
503
|
+
pi.options.array_class = Qnil;
|
504
|
+
} else {
|
505
|
+
rb_check_type(v, T_CLASS);
|
506
|
+
pi.options.array_class = v;
|
507
|
+
}
|
508
|
+
}
|
509
|
+
v = rb_hash_lookup(ropts, oj_max_nesting_sym);
|
510
|
+
if (Qtrue == v) {
|
511
|
+
pi.max_depth = 100;
|
512
|
+
} else if (Qfalse == v || Qnil == v) {
|
513
|
+
pi.max_depth = 0;
|
514
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
515
|
+
pi.max_depth = NUM2INT(v);
|
516
|
+
}
|
517
|
+
oj_parse_opt_match_string(&pi.options.str_rx, ropts);
|
518
|
+
if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
|
519
|
+
rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");
|
520
|
+
}
|
521
|
+
}
|
522
|
+
*args = *argv;
|
523
|
+
|
524
|
+
return oj_pi_parse(1, args, &pi, 0, 0, 0);
|
525
|
+
}
|
526
|
+
|
527
|
+
/* Document-method: parse
|
528
|
+
* call-seq: parse(source, opts=nil)
|
529
|
+
*
|
530
|
+
* Parses a JSON String or IO into a Ruby Object. Options other than those
|
531
|
+
* listed such as +:allow_nan+ or +:max_nesting+ are ignored. +:object_class+ and
|
532
|
+
* +:array_object+ are not supported.
|
533
|
+
*
|
534
|
+
* - *source* [_String_|IO] source to parse
|
535
|
+
* - *opts* [_Hash_] options
|
536
|
+
* - *:symbolize* [Boolean] _names flag indicating JSON object keys should be Symbols instead of Strings
|
537
|
+
* - *:create_additions* [Boolean] flag indicating a key matching +create_id+ in a JSON object should trigger the creation of Ruby Object
|
538
|
+
*
|
539
|
+
* Returns [Object]
|
540
|
+
* @see create_id=
|
541
|
+
*/
|
542
|
+
VALUE
|
543
|
+
oj_mimic_parse(int argc, VALUE *argv, VALUE self) {
|
544
|
+
return mimic_parse_core(argc, argv, self, false);
|
545
|
+
}
|
546
|
+
|
547
|
+
/* Document-method: parse!
|
548
|
+
* call-seq: parse!(source, opts=nil)
|
549
|
+
*
|
550
|
+
* Same as parse().
|
551
|
+
* @see parse
|
552
|
+
*/
|
553
|
+
static VALUE
|
554
|
+
mimic_parse_bang(int argc, VALUE *argv, VALUE self) {
|
555
|
+
return mimic_parse_core(argc, argv, self, true);
|
556
|
+
}
|
557
|
+
|
558
|
+
/* Document-method: recurse_proc
|
559
|
+
* call-seq: recurse_proc(obj, &proc)
|
560
|
+
*
|
561
|
+
* Yields to the proc for every element in the obj recursivly.
|
562
|
+
*
|
563
|
+
* - *obj* [_Hash_|Array] object to walk
|
564
|
+
* - *proc* [_Proc_] to yield to on each element
|
565
|
+
*/
|
566
|
+
static VALUE
|
567
|
+
mimic_recurse_proc(VALUE self, VALUE obj) {
|
568
|
+
rb_need_block();
|
569
|
+
mimic_walk(Qnil, obj, Qnil);
|
570
|
+
|
571
|
+
return Qnil;
|
572
|
+
}
|
573
|
+
|
574
|
+
/* Document-method: create_id=
|
575
|
+
* call-seq: create_id=(id)
|
576
|
+
*
|
577
|
+
* Sets the create_id tag to look for in JSON document. That key triggers the
|
578
|
+
* creation of a class with the same name.
|
579
|
+
*
|
580
|
+
* - *id* [_nil_|String] new create_id
|
581
|
+
*
|
582
|
+
* Returns [_String_] the id.
|
583
|
+
*/
|
584
|
+
static VALUE
|
585
|
+
mimic_set_create_id(VALUE self, VALUE id) {
|
586
|
+
Check_Type(id, T_STRING);
|
587
|
+
|
588
|
+
if (0 != oj_default_options.create_id) {
|
589
|
+
if (json_class != oj_default_options.create_id) {
|
590
|
+
xfree((char*)oj_default_options.create_id);
|
591
|
+
}
|
592
|
+
oj_default_options.create_id = 0;
|
593
|
+
oj_default_options.create_id_len = 0;
|
594
|
+
}
|
595
|
+
if (Qnil != id) {
|
596
|
+
size_t len = RSTRING_LEN(id) + 1;
|
597
|
+
|
598
|
+
oj_default_options.create_id = ALLOC_N(char, len);
|
599
|
+
strcpy((char*)oj_default_options.create_id, StringValuePtr(id));
|
600
|
+
oj_default_options.create_id_len = len - 1;
|
601
|
+
}
|
602
|
+
return id;
|
603
|
+
}
|
604
|
+
|
605
|
+
/* Document-method: create_id
|
606
|
+
* call-seq: create_id()
|
607
|
+
*
|
608
|
+
* Returns [_String_] the create_id.
|
609
|
+
*/
|
610
|
+
static VALUE
|
611
|
+
mimic_create_id(VALUE self) {
|
612
|
+
if (0 != oj_default_options.create_id) {
|
613
|
+
return oj_encode(rb_str_new_cstr(oj_default_options.create_id));
|
614
|
+
}
|
615
|
+
return rb_str_new_cstr(json_class);
|
616
|
+
}
|
617
|
+
|
618
|
+
static struct _Options mimic_object_to_json_options = {
|
619
|
+
0, // indent
|
620
|
+
No, // circular
|
621
|
+
No, // auto_define
|
622
|
+
No, // sym_key
|
623
|
+
JXEsc, // escape_mode
|
624
|
+
CompatMode, // mode
|
625
|
+
No, // class_cache
|
626
|
+
RubyTime, // time_format
|
627
|
+
No, // bigdec_as_num
|
628
|
+
FloatDec, // bigdec_load
|
629
|
+
No, // to_hash
|
630
|
+
No, // to_json
|
631
|
+
No, // as_json
|
632
|
+
No, // nilnil
|
633
|
+
Yes, // empty_string
|
634
|
+
Yes, // allow_gc
|
635
|
+
Yes, // quirks_mode
|
636
|
+
No, // allow_invalid
|
637
|
+
No, // create_ok
|
638
|
+
No, // allow_nan
|
639
|
+
json_class, // create_id
|
640
|
+
10, // create_id_len
|
641
|
+
3, // sec_prec
|
642
|
+
16, // float_prec
|
643
|
+
"%0.15g", // float_fmt
|
644
|
+
Qnil, // hash_class
|
645
|
+
Qnil, // array_class
|
646
|
+
{ // dump_opts
|
647
|
+
false, //use
|
648
|
+
"", // indent
|
649
|
+
"", // before_sep
|
650
|
+
"", // after_sep
|
651
|
+
"", // hash_nl
|
652
|
+
"", // array_nl
|
653
|
+
0, // indent_size
|
654
|
+
0, // before_size
|
655
|
+
0, // after_size
|
656
|
+
0, // hash_size
|
657
|
+
0, // array_size
|
658
|
+
AutoNan,// nan_dump
|
659
|
+
false, // omit_nil
|
660
|
+
100, // max_depth
|
661
|
+
},
|
662
|
+
{ // str_rx
|
663
|
+
NULL, // head
|
664
|
+
NULL, // tail
|
665
|
+
{ '\0' }, // err
|
666
|
+
}
|
667
|
+
};
|
668
|
+
|
669
|
+
static VALUE
|
670
|
+
mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
|
671
|
+
char buf[4096];
|
672
|
+
struct _Out out;
|
673
|
+
VALUE rstr;
|
674
|
+
struct _Options copts = oj_default_options;
|
675
|
+
|
676
|
+
copts.str_rx.head = NULL;
|
677
|
+
copts.str_rx.tail = NULL;
|
678
|
+
out.buf = buf;
|
679
|
+
out.end = buf + sizeof(buf) - 10;
|
680
|
+
out.allocated = 0;
|
681
|
+
out.omit_nil = copts.dump_opts.omit_nil;
|
682
|
+
copts.mode = CompatMode;
|
683
|
+
copts.to_json = No;
|
684
|
+
if (1 <= argc && Qnil != argv[0]) {
|
685
|
+
oj_parse_mimic_dump_options(argv[0], &copts);
|
686
|
+
}
|
687
|
+
// To be strict the mimic_object_to_json_options should be used but people
|
688
|
+
// seem to prefer the option of changing that.
|
689
|
+
//oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
|
690
|
+
oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
|
691
|
+
if (0 == out.buf) {
|
692
|
+
rb_raise(rb_eNoMemError, "Not enough memory.");
|
693
|
+
}
|
694
|
+
rstr = rb_str_new2(out.buf);
|
695
|
+
rstr = oj_encode(rstr);
|
696
|
+
if (out.allocated) {
|
697
|
+
xfree(out.buf);
|
698
|
+
}
|
699
|
+
return rstr;
|
700
|
+
}
|
701
|
+
|
702
|
+
/* Document-method: state
|
703
|
+
* call-seq: state()
|
704
|
+
*
|
705
|
+
* Returns [_JSON::State_] the JSON::State class.
|
706
|
+
*/
|
707
|
+
static VALUE
|
708
|
+
mimic_state(VALUE self) {
|
709
|
+
return state_class;
|
710
|
+
}
|
711
|
+
|
712
|
+
/* Document-module: JSON
|
713
|
+
*
|
714
|
+
* A mimic of the json gem module.
|
715
|
+
*/
|
716
|
+
VALUE
|
717
|
+
oj_define_mimic_json(int argc, VALUE *argv, VALUE self) {
|
718
|
+
VALUE ext;
|
719
|
+
VALUE dummy;
|
720
|
+
VALUE verbose;
|
721
|
+
VALUE json_error;
|
722
|
+
VALUE json;
|
723
|
+
VALUE generator;
|
724
|
+
|
725
|
+
// Either set the paths to indicate JSON has been loaded or replaces the
|
726
|
+
// methods if it has been loaded.
|
727
|
+
if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
|
728
|
+
json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
729
|
+
} else {
|
730
|
+
json = rb_define_module("JSON");
|
731
|
+
}
|
732
|
+
verbose = rb_gv_get("$VERBOSE");
|
733
|
+
rb_gv_set("$VERBOSE", Qfalse);
|
734
|
+
rb_define_module_function(rb_cObject, "JSON", mimic_dump_load, -1);
|
735
|
+
dummy = rb_gv_get("$LOADED_FEATURES");
|
736
|
+
if (rb_type(dummy) == T_ARRAY) {
|
737
|
+
rb_ary_push(dummy, rb_str_new2("json"));
|
738
|
+
if (0 < argc) {
|
739
|
+
VALUE mimic_args[1];
|
740
|
+
|
741
|
+
*mimic_args = *argv;
|
742
|
+
rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
|
743
|
+
} else {
|
744
|
+
rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
|
745
|
+
}
|
746
|
+
}
|
747
|
+
if (rb_const_defined_at(json, rb_intern("Ext"))) {
|
748
|
+
ext = rb_const_get_at(json, rb_intern("Ext"));
|
749
|
+
} else {
|
750
|
+
ext = rb_define_module_under(json, "Ext");
|
751
|
+
}
|
752
|
+
if (rb_const_defined_at(ext, rb_intern("Generator"))) {
|
753
|
+
generator = rb_const_get_at(ext, rb_intern("Generator"));
|
754
|
+
} else {
|
755
|
+
generator = rb_define_module_under(ext, "Generator");
|
756
|
+
}
|
757
|
+
|
758
|
+
// convince Ruby that the json gem has already been loaded
|
759
|
+
// Pull in the JSON::State mimic file.
|
760
|
+
rb_require("oj/state");
|
761
|
+
state_class = rb_const_get_at(generator, rb_intern("State"));
|
762
|
+
// TBD create all modules in mimic_loaded
|
763
|
+
|
764
|
+
rb_define_module_function(json, "create_id=", mimic_set_create_id, 1);
|
765
|
+
rb_define_module_function(json, "create_id", mimic_create_id, 0);
|
766
|
+
|
767
|
+
rb_define_module_function(json, "dump", mimic_dump, -1);
|
768
|
+
rb_define_module_function(json, "load", mimic_load, -1);
|
769
|
+
rb_define_module_function(json, "restore", mimic_load, -1);
|
770
|
+
rb_define_module_function(json, "recurse_proc", mimic_recurse_proc, 1);
|
771
|
+
rb_define_module_function(json, "[]", mimic_dump_load, -1);
|
772
|
+
|
773
|
+
rb_define_module_function(json, "generate", oj_mimic_generate, -1);
|
774
|
+
rb_define_module_function(json, "fast_generate", oj_mimic_generate, -1);
|
775
|
+
rb_define_module_function(json, "pretty_generate", oj_mimic_pretty_generate, -1);
|
776
|
+
// For older versions of JSON, the deprecated unparse methods.
|
777
|
+
rb_define_module_function(json, "unparse", oj_mimic_generate, -1);
|
778
|
+
rb_define_module_function(json, "fast_unparse", oj_mimic_generate, -1);
|
779
|
+
rb_define_module_function(json, "pretty_unparse", oj_mimic_pretty_generate, -1);
|
780
|
+
|
781
|
+
rb_define_module_function(json, "parse", oj_mimic_parse, -1);
|
782
|
+
rb_define_module_function(json, "parse!", mimic_parse_bang, -1);
|
783
|
+
|
784
|
+
rb_define_module_function(json, "state", mimic_state, 0);
|
785
|
+
|
786
|
+
rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
|
787
|
+
|
788
|
+
rb_gv_set("$VERBOSE", verbose);
|
789
|
+
|
790
|
+
symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
|
791
|
+
|
792
|
+
if (rb_const_defined_at(json, rb_intern("JSONError"))) {
|
793
|
+
json_error = rb_const_get(json, rb_intern("JSONError"));
|
794
|
+
} else {
|
795
|
+
json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
|
796
|
+
}
|
797
|
+
if (rb_const_defined_at(json, rb_intern("ParserError"))) {
|
798
|
+
oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
|
799
|
+
} else {
|
800
|
+
oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
|
801
|
+
}
|
802
|
+
if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
|
803
|
+
oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
|
804
|
+
} else {
|
805
|
+
oj_json_generator_error_class = rb_define_class_under(json, "GeneratorError", json_error);
|
806
|
+
}
|
807
|
+
if (rb_const_defined_at(json, rb_intern("NestingError"))) {
|
808
|
+
rb_const_get(json, rb_intern("NestingError"));
|
809
|
+
} else {
|
810
|
+
rb_define_class_under(json, "NestingError", json_error);
|
811
|
+
}
|
812
|
+
|
813
|
+
oj_default_options = mimic_object_to_json_options;
|
814
|
+
oj_default_options.to_json = Yes;
|
815
|
+
|
816
|
+
return json;
|
817
|
+
}
|