oj 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- data/README.md +4 -2
- data/ext/oj/load.c +5 -1
- data/ext/oj/oj.c +52 -7
- data/ext/oj/oj.h +4 -0
- data/lib/oj/version.rb +1 -1
- data/test/perf.rb +1 -1
- data/test/tests.rb +28 -0
- metadata +1 -1
data/README.md
CHANGED
@@ -24,9 +24,11 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
24
24
|
|
25
25
|
## <a name="release">Release Notes</a>
|
26
26
|
|
27
|
-
### Release 1.0.
|
27
|
+
### Release 1.0.3
|
28
28
|
|
29
|
-
- Added
|
29
|
+
- Added :symbol_keys option to convert String hash keys into Symbols.
|
30
|
+
|
31
|
+
- The load() method now supports IO Objects as input as well as Strings.
|
30
32
|
|
31
33
|
## <a name="description">Description</a>
|
32
34
|
|
data/ext/oj/load.c
CHANGED
@@ -452,7 +452,11 @@ read_obj(ParseInfo pi) {
|
|
452
452
|
}
|
453
453
|
rb_ivar_set(obj, var_id, val);
|
454
454
|
} else if (T_HASH == obj_type) {
|
455
|
-
|
455
|
+
if (Yes == pi->options->sym_key) {
|
456
|
+
rb_hash_aset(obj, rb_str_intern(key), val);
|
457
|
+
} else {
|
458
|
+
rb_hash_aset(obj, key, val);
|
459
|
+
}
|
456
460
|
if ((CompatMode == pi->options->mode || ObjectMode == pi->options->mode) &&
|
457
461
|
0 == json_class_name &&
|
458
462
|
0 != ks && 'j' == *ks && 0 == strcmp("json_class", ks) &&
|
data/ext/oj/oj.c
CHANGED
@@ -49,6 +49,8 @@ ID oj_as_json_id;
|
|
49
49
|
ID oj_at_id;
|
50
50
|
ID oj_instance_variables_id;
|
51
51
|
ID oj_json_create_id;
|
52
|
+
ID oj_read_id;
|
53
|
+
ID oj_string_id;
|
52
54
|
ID oj_to_hash_id;
|
53
55
|
ID oj_to_json_id;
|
54
56
|
ID oj_to_sym_id;
|
@@ -57,6 +59,8 @@ ID oj_tv_sec_id;
|
|
57
59
|
ID oj_tv_usec_id;
|
58
60
|
|
59
61
|
VALUE oj_bag_class;
|
62
|
+
VALUE oj_date_class;
|
63
|
+
VALUE oj_stringio_class;
|
60
64
|
VALUE oj_struct_class;
|
61
65
|
VALUE oj_time_class;
|
62
66
|
|
@@ -71,6 +75,7 @@ static VALUE mode_sym;
|
|
71
75
|
static VALUE null_sym;
|
72
76
|
static VALUE object_sym;
|
73
77
|
static VALUE strict_sym;
|
78
|
+
static VALUE symbol_keys_sym;
|
74
79
|
|
75
80
|
Cache oj_class_cache = 0;
|
76
81
|
Cache oj_attr_cache = 0;
|
@@ -80,6 +85,7 @@ struct _Options oj_default_options = {
|
|
80
85
|
0, // indent
|
81
86
|
No, // circular
|
82
87
|
Yes, // auto_define
|
88
|
+
No, // sym_key
|
83
89
|
ObjectMode, // mode
|
84
90
|
};
|
85
91
|
|
@@ -90,6 +96,7 @@ struct _Options oj_default_options = {
|
|
90
96
|
* - encoding: [String] character encoding for the JSON file
|
91
97
|
* - circular: [true|false|nil] support circular references while dumping
|
92
98
|
* - auto_define: [true|false|nil] automatically define classes if they do not exist
|
99
|
+
* - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
|
93
100
|
* - mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
|
94
101
|
* @return [Hash] all current option settings.
|
95
102
|
*/
|
@@ -102,6 +109,7 @@ get_def_opts(VALUE self) {
|
|
102
109
|
rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
|
103
110
|
rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
|
104
111
|
rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
|
112
|
+
rb_hash_aset(opts, symbol_keys_sym, (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
|
105
113
|
switch (oj_default_options.mode) {
|
106
114
|
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
|
107
115
|
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
|
@@ -120,6 +128,7 @@ get_def_opts(VALUE self) {
|
|
120
128
|
* @param [String] :encoding character encoding for the JSON file
|
121
129
|
* @param [true|false|nil] :circular support circular references while dumping
|
122
130
|
* @param [true|false|nil] :auto_define automatically define classes if they do not exist
|
131
|
+
* @param [true|false|nil] :symbol_keys convert hash keys to symbols
|
123
132
|
* @param [:object|:strict|:compat|:null] load and dump mode to use for JSON
|
124
133
|
* :strict raises an exception when a non-supported Object is
|
125
134
|
* encountered. :compat attempts to extract variable values from an
|
@@ -134,6 +143,7 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
134
143
|
struct _YesNoOpt ynos[] = {
|
135
144
|
{ circular_sym, &oj_default_options.circular },
|
136
145
|
{ auto_define_sym, &oj_default_options.auto_define },
|
146
|
+
{ symbol_keys_sym, &oj_default_options.sym_key },
|
137
147
|
{ Qnil, 0 }
|
138
148
|
};
|
139
149
|
YesNoOpt o;
|
@@ -194,6 +204,7 @@ parse_options(VALUE ropts, Options copts) {
|
|
194
204
|
struct _YesNoOpt ynos[] = {
|
195
205
|
{ circular_sym, &copts->circular },
|
196
206
|
{ auto_define_sym, &copts->auto_define },
|
207
|
+
{ symbol_keys_sym, &copts->sym_key },
|
197
208
|
{ Qnil, 0 }
|
198
209
|
};
|
199
210
|
YesNoOpt o;
|
@@ -268,13 +279,38 @@ static VALUE
|
|
268
279
|
load_str(int argc, VALUE *argv, VALUE self) {
|
269
280
|
char *json;
|
270
281
|
size_t len;
|
282
|
+
VALUE input;
|
271
283
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
284
|
+
if (1 > argc) {
|
285
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to load().\n");
|
286
|
+
}
|
287
|
+
input = *argv;
|
288
|
+
if (rb_type(input) == T_STRING) {
|
289
|
+
// the json string gets modified so make a copy of it
|
290
|
+
len = RSTRING_LEN(*argv) + 1;
|
291
|
+
json = ALLOCA_N(char, len);
|
292
|
+
strcpy(json, StringValuePtr(*argv));
|
293
|
+
} else {
|
294
|
+
VALUE clas = rb_obj_class(input);
|
295
|
+
VALUE s;
|
296
|
+
|
297
|
+
if (oj_stringio_class == clas) {
|
298
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
299
|
+
len = RSTRING_LEN(s) + 1;
|
300
|
+
json = ALLOCA_N(char, len);
|
301
|
+
strcpy(json, StringValuePtr(s));
|
302
|
+
|
303
|
+
// TBD else responds to fileno
|
304
|
+
|
305
|
+
} else if (rb_respond_to(input, oj_read_id)) {
|
306
|
+
s = rb_funcall2(input, oj_read_id, 0, 0);
|
307
|
+
len = RSTRING_LEN(s) + 1;
|
308
|
+
json = ALLOCA_N(char, len);
|
309
|
+
strcpy(json, StringValuePtr(s));
|
310
|
+
} else {
|
311
|
+
rb_raise(rb_eArgError, "load() expected a String or IO Object.\n");
|
312
|
+
}
|
313
|
+
}
|
278
314
|
return load(json, argc - 1, argv + 1, self);
|
279
315
|
}
|
280
316
|
|
@@ -365,6 +401,10 @@ void Init_oj() {
|
|
365
401
|
Oj = rb_define_module("Oj");
|
366
402
|
keep = rb_cv_get(Oj, "@@keep"); // needed to stop GC from deleting and reusing VALUEs
|
367
403
|
|
404
|
+
rb_require("time");
|
405
|
+
rb_require("date");
|
406
|
+
rb_require("stringio");
|
407
|
+
|
368
408
|
rb_define_module_function(Oj, "default_options", get_def_opts, 0);
|
369
409
|
rb_define_module_function(Oj, "default_options=", set_def_opts, 1);
|
370
410
|
|
@@ -377,16 +417,20 @@ void Init_oj() {
|
|
377
417
|
oj_at_id = rb_intern("at");
|
378
418
|
oj_instance_variables_id = rb_intern("instance_variables");
|
379
419
|
oj_json_create_id = rb_intern("json_create");
|
420
|
+
oj_read_id = rb_intern("read");
|
421
|
+
oj_string_id = rb_intern("string");
|
380
422
|
oj_to_hash_id = rb_intern("to_hash");
|
381
423
|
oj_to_json_id = rb_intern("to_json");
|
382
424
|
oj_to_sym_id = rb_intern("to_sym");
|
383
425
|
oj_tv_nsec_id = rb_intern("tv_nsec");
|
384
426
|
oj_tv_sec_id = rb_intern("tv_sec");
|
385
427
|
oj_tv_usec_id = rb_intern("tv_usec");
|
386
|
-
|
428
|
+
|
387
429
|
oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
|
388
430
|
oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
|
389
431
|
oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
|
432
|
+
oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
|
433
|
+
oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
390
434
|
|
391
435
|
auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
|
392
436
|
circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
|
@@ -394,6 +438,7 @@ void Init_oj() {
|
|
394
438
|
encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
|
395
439
|
indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
|
396
440
|
mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
|
441
|
+
symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_ary_push(keep, symbol_keys_sym);
|
397
442
|
null_sym = ID2SYM(rb_intern("null")); rb_ary_push(keep, null_sym);
|
398
443
|
object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
|
399
444
|
strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
|
data/ext/oj/oj.h
CHANGED
@@ -84,6 +84,7 @@ typedef struct _Options {
|
|
84
84
|
int indent; // indention for dump, default 2
|
85
85
|
char circular; // YesNo
|
86
86
|
char auto_define; // YesNo
|
87
|
+
char sym_key; // YesNo
|
87
88
|
char mode; // Mode
|
88
89
|
} *Options;
|
89
90
|
|
@@ -99,7 +100,9 @@ extern VALUE Oj;
|
|
99
100
|
extern struct _Options oj_default_options;
|
100
101
|
|
101
102
|
extern VALUE oj_bag_class;
|
103
|
+
extern VALUE oj_date_class;
|
102
104
|
extern VALUE oj_doc_class;
|
105
|
+
extern VALUE oj_stringio_class;
|
103
106
|
extern VALUE oj_struct_class;
|
104
107
|
extern VALUE oj_time_class;
|
105
108
|
|
@@ -109,6 +112,7 @@ extern ID oj_as_json_id;
|
|
109
112
|
extern ID oj_at_id;
|
110
113
|
extern ID oj_instance_variables_id;
|
111
114
|
extern ID oj_json_create_id;
|
115
|
+
extern ID oj_string_id;
|
112
116
|
extern ID oj_to_hash_id;
|
113
117
|
extern ID oj_to_json_id;
|
114
118
|
extern ID oj_to_sym_id;
|
data/lib/oj/version.rb
CHANGED
data/test/perf.rb
CHANGED
@@ -54,7 +54,7 @@ class Perf
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
puts
|
57
|
-
puts "Comparison Matrix\n(performance factor, 2.0 row is
|
57
|
+
puts "Comparison Matrix\n(performance factor, 2.0 means row is twice as fast as column)"
|
58
58
|
puts ([' ' * width] + iva.map { |i| "%*s" % [width, i.title] }).join(' ')
|
59
59
|
puts (['-' * width] + iva.map { |i| '-' * width }).join(' ')
|
60
60
|
iva.each do |i|
|
data/test/tests.rb
CHANGED
@@ -5,6 +5,7 @@ $: << File.join(File.dirname(__FILE__), "../lib")
|
|
5
5
|
$: << File.join(File.dirname(__FILE__), "../ext")
|
6
6
|
|
7
7
|
require 'test/unit'
|
8
|
+
require 'stringio'
|
8
9
|
require 'oj'
|
9
10
|
|
10
11
|
def hash_eql(h1, h2)
|
@@ -70,6 +71,7 @@ class Juice < ::Test::Unit::TestCase
|
|
70
71
|
:indent=>0,
|
71
72
|
:circular=>false,
|
72
73
|
:auto_define=>true,
|
74
|
+
:symbol_keys=>false,
|
73
75
|
:mode=>:object}, opts)
|
74
76
|
end
|
75
77
|
|
@@ -79,12 +81,14 @@ class Juice < ::Test::Unit::TestCase
|
|
79
81
|
:indent=>0,
|
80
82
|
:circular=>false,
|
81
83
|
:auto_define=>true,
|
84
|
+
:symbol_keys=>false,
|
82
85
|
:mode=>:object}
|
83
86
|
o2 = {
|
84
87
|
:encoding=>"UTF-8",
|
85
88
|
:indent=>4,
|
86
89
|
:circular=>true,
|
87
90
|
:auto_define=>false,
|
91
|
+
:symbol_keys=>false,
|
88
92
|
:mode=>:compat}
|
89
93
|
o3 = { :indent => 4 }
|
90
94
|
Oj.default_options = o2
|
@@ -471,6 +475,30 @@ class Juice < ::Test::Unit::TestCase
|
|
471
475
|
assert_equal(h['b'].__id__, obj.__id__)
|
472
476
|
end
|
473
477
|
|
478
|
+
# Stream IO
|
479
|
+
def test_string_io
|
480
|
+
json = %{{
|
481
|
+
"x":true,
|
482
|
+
"y":58,
|
483
|
+
"z": [1,2,3]
|
484
|
+
}
|
485
|
+
}
|
486
|
+
input = StringIO.new(json)
|
487
|
+
obj = Oj.load(input, :mode => :strict)
|
488
|
+
assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
|
489
|
+
end
|
490
|
+
|
491
|
+
def test_symbol_keys
|
492
|
+
json = %{{
|
493
|
+
"x":true,
|
494
|
+
"y":58,
|
495
|
+
"z": [1,2,3]
|
496
|
+
}
|
497
|
+
}
|
498
|
+
obj = Oj.load(json, :mode => :strict, :symbol_keys => true)
|
499
|
+
assert_equal({ :x => true, :y => 58, :z => [1, 2, 3]}, obj)
|
500
|
+
end
|
501
|
+
|
474
502
|
def dump_and_load(obj, trace=false)
|
475
503
|
json = Oj.dump(obj, :indent => 2)
|
476
504
|
puts json if trace
|