oj 0.7.0 → 0.8.0
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 +57 -9
- data/ext/oj/dump.c +38 -6
- data/ext/oj/load.c +14 -31
- data/ext/oj/oj.c +10 -1
- data/ext/oj/oj.h +6 -4
- data/lib/oj.rb +2 -1
- data/lib/oj/bag.rb +92 -0
- data/lib/oj/version.rb +1 -1
- data/test/tests.rb +19 -0
- metadata +3 -2
data/README.md
CHANGED
@@ -22,13 +22,9 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
22
22
|
|
23
23
|
## <a name="release">Release Notes</a>
|
24
24
|
|
25
|
-
### Release 0.
|
25
|
+
### Release 0.8.0
|
26
26
|
|
27
|
-
-
|
28
|
-
|
29
|
-
- serialized Ruby Objects can now be deserialized
|
30
|
-
|
31
|
-
- improved performance testing
|
27
|
+
- Auto creation of data classes when unmarshalling Objects if the Class is not defined
|
32
28
|
|
33
29
|
## <a name="description">Description</a>
|
34
30
|
|
@@ -57,11 +53,13 @@ to_hash() is more flexible and produces more consistent output so it has a
|
|
57
53
|
preference over the to_json() method. If neither the to_json() or to_hash()
|
58
54
|
methods exist then the Oj internal Object variable encoding is used.
|
59
55
|
|
60
|
-
|
56
|
+
Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, and RBX.
|
61
57
|
|
62
|
-
|
58
|
+
## <a name="plans">Planned Releases</a>
|
63
59
|
|
64
|
-
|
60
|
+
- Release 0.9: Support for circular references.
|
61
|
+
|
62
|
+
- Release 1.0: A JSON stream parser.
|
65
63
|
|
66
64
|
## <a name="compare">Comparisons</a>
|
67
65
|
|
@@ -81,6 +79,9 @@ Object encoding and without Bignums.
|
|
81
79
|
None of the packages except Oj were able to serialize Ruby Objects that did
|
82
80
|
not have a to_json() method or were of the 7 native JSON types.
|
83
81
|
|
82
|
+
A perf_obj.rb file was added for comparing different Object marshalling
|
83
|
+
packages.
|
84
|
+
|
84
85
|
It is also worth noting that although Oj is slightly behind MessagePack for
|
85
86
|
parsing, Oj serialization is much faster than MessagePack even though Oj uses
|
86
87
|
human readable JSON vs the binary MessagePack format.
|
@@ -227,3 +228,50 @@ without Objects or numbers (for JSON Pure) JSON:
|
|
227
228
|
h2 = Oj.parse(json)
|
228
229
|
puts "Same? #{h == h2}"
|
229
230
|
# true
|
231
|
+
|
232
|
+
### Object JSON format:
|
233
|
+
|
234
|
+
In :object mode Oj generates JSON that follows conventions which allow Class
|
235
|
+
and other information such as Object IDs for circular reference detection. The
|
236
|
+
formating follows the following rules.
|
237
|
+
|
238
|
+
1. JSON native types, true, false, nil, String, Hash, Array, and Number are
|
239
|
+
encoded normally.
|
240
|
+
|
241
|
+
2. If a Hash uses Symbols as keys those keys appear as Strings with a leading
|
242
|
+
':' character.
|
243
|
+
|
244
|
+
3. The '^' character denotes a special key value when in a JSON Object sequence.
|
245
|
+
|
246
|
+
4. If a String begins with a ':' character such as ':abc' it is encoded as {"^s":":abc"}.
|
247
|
+
|
248
|
+
5. If a Symbol begins with a ':' character such as :":abc" is is encoded as {"^m":":abc"}.
|
249
|
+
|
250
|
+
6. A "^c" JSON Object key indicates the value should be converted to a Ruby
|
251
|
+
class. The sequence {"^c":"Oj::Bag"} is read as the Oj::Bag class.
|
252
|
+
|
253
|
+
7. A "^t" JSON Object key indicates the value should be converted to a Ruby
|
254
|
+
Time. The sequence {"^t":1325775487.000000} is read as Jan 5, 2012 at
|
255
|
+
23:58:07.
|
256
|
+
|
257
|
+
8. A "^o" JSON Object key indicates the value should be converted to a Ruby
|
258
|
+
Object. The first entry in the JSON Object must be a class with the "^o"
|
259
|
+
key. After that each entry is treated as a variable of the Object where the
|
260
|
+
key is the variable name without the preceeding '@'. An example is
|
261
|
+
{"^o":"Oj::Bag","x":58,"y":"marbles"}.
|
262
|
+
|
263
|
+
9. When encoding an Object, if the variable name does not begin with an '@'
|
264
|
+
character then the name preceeded by a '~' character. This occurs in the
|
265
|
+
Exception class. An example is {"^o":"StandardError","~mesg":"A
|
266
|
+
Message","~bt":[".\/tests.rb:345:in `test_exception'"]}
|
267
|
+
|
268
|
+
10. If a Hash entry has a key that is not a String or Symbol then the entry is
|
269
|
+
encoded with a key of the form "^#n" where n is a hex number. The value that
|
270
|
+
is an Array where the first element is the key in the Hash and the second is
|
271
|
+
the value. An example is {"^#3":[2,5]}.
|
272
|
+
|
273
|
+
11. A "^i" JSON entry in either an Object or Array is the ID of the Ruby
|
274
|
+
Object being encoded. It is used when the :circular flag is set. It can appear
|
275
|
+
in either a JSON Object or in a JSON Array. If alone it represented a link to
|
276
|
+
the original Hash or JSON. If an added attribute it is the ID of the original
|
277
|
+
Object or Array. Examples are TBD.
|
data/ext/oj/dump.c
CHANGED
@@ -83,7 +83,8 @@ static void dump_bignum(VALUE obj, Out out);
|
|
83
83
|
static void dump_float(VALUE obj, Out out);
|
84
84
|
static void dump_cstr(const char *str, size_t cnt, int is_sym, Out out);
|
85
85
|
static void dump_hex(u_char c, Out out);
|
86
|
-
static void
|
86
|
+
static void dump_str_comp(VALUE obj, Out out);
|
87
|
+
static void dump_str_obj(VALUE obj, Out out);
|
87
88
|
static void dump_sym_comp(VALUE obj, Out out);
|
88
89
|
static void dump_sym_obj(VALUE obj, Out out);
|
89
90
|
static void dump_class_comp(VALUE obj, Out out);
|
@@ -350,10 +351,33 @@ dump_cstr(const char *str, size_t cnt, int is_sym, Out out) {
|
|
350
351
|
}
|
351
352
|
|
352
353
|
static void
|
353
|
-
|
354
|
+
dump_str_comp(VALUE obj, Out out) {
|
354
355
|
dump_cstr(StringValuePtr(obj), RSTRING_LEN(obj), 0, out);
|
355
356
|
}
|
356
357
|
|
358
|
+
static void
|
359
|
+
dump_str_obj(VALUE obj, Out out) {
|
360
|
+
const char *s = StringValuePtr(obj);
|
361
|
+
size_t len = RSTRING_LEN(obj);
|
362
|
+
|
363
|
+
if (':' == *s) {
|
364
|
+
if (out->end - out->cur <= 6) {
|
365
|
+
grow(out, 6);
|
366
|
+
}
|
367
|
+
*out->cur++ = '{';
|
368
|
+
*out->cur++ = '"';
|
369
|
+
*out->cur++ = '^';
|
370
|
+
*out->cur++ = 's';
|
371
|
+
*out->cur++ = '"';
|
372
|
+
*out->cur++ = ':';
|
373
|
+
dump_cstr(s, len, 0, out);
|
374
|
+
*out->cur++ = '}';
|
375
|
+
*out->cur = '\0';
|
376
|
+
} else {
|
377
|
+
dump_cstr(s, len, 0, out);
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
357
381
|
static void
|
358
382
|
dump_sym_comp(VALUE obj, Out out) {
|
359
383
|
const char *sym = rb_id2name(SYM2ID(obj));
|
@@ -373,7 +397,7 @@ dump_sym_obj(VALUE obj, Out out) {
|
|
373
397
|
*out->cur++ = '{';
|
374
398
|
*out->cur++ = '"';
|
375
399
|
*out->cur++ = '^';
|
376
|
-
*out->cur++ = '
|
400
|
+
*out->cur++ = 'm';
|
377
401
|
*out->cur++ = '"';
|
378
402
|
*out->cur++ = ':';
|
379
403
|
dump_cstr(sym, len, 0, out);
|
@@ -456,7 +480,7 @@ hash_cb_strict(VALUE key, VALUE value, Out out) {
|
|
456
480
|
}
|
457
481
|
fill_indent(out, depth);
|
458
482
|
if (rb_type(key) == T_STRING) {
|
459
|
-
|
483
|
+
dump_str_comp(key, out);
|
460
484
|
*out->cur++ = ':';
|
461
485
|
dump_val(value, depth, out);
|
462
486
|
} else {
|
@@ -479,7 +503,7 @@ hash_cb_object(VALUE key, VALUE value, Out out) {
|
|
479
503
|
fill_indent(out, depth);
|
480
504
|
// TBD if key is a string else dump with unique key for and entry array
|
481
505
|
if (rb_type(key) == T_STRING) {
|
482
|
-
|
506
|
+
dump_str_obj(key, out);
|
483
507
|
*out->cur++ = ':';
|
484
508
|
dump_val(value, depth, out);
|
485
509
|
} else if (rb_type(key) == T_SYMBOL) {
|
@@ -778,7 +802,15 @@ dump_val(VALUE obj, int depth, Out out) {
|
|
778
802
|
case T_FIXNUM: dump_fixnum(obj, out); break;
|
779
803
|
case T_FLOAT: dump_float(obj, out); break;
|
780
804
|
case T_BIGNUM: dump_bignum(obj, out); break;
|
781
|
-
case T_STRING:
|
805
|
+
case T_STRING:
|
806
|
+
switch (out->opts->mode) {
|
807
|
+
case StrictMode:
|
808
|
+
case NullMode:
|
809
|
+
case CompatMode: dump_str_comp(obj, out); break;
|
810
|
+
case ObjectMode:
|
811
|
+
default: dump_str_obj(obj, out); break;
|
812
|
+
}
|
813
|
+
break;
|
782
814
|
case T_SYMBOL:
|
783
815
|
switch (out->opts->mode) {
|
784
816
|
case StrictMode: raise_strict(obj); break;
|
data/ext/oj/load.c
CHANGED
@@ -112,15 +112,14 @@ next_white(ParseInfo pi) {
|
|
112
112
|
}
|
113
113
|
|
114
114
|
inline static VALUE
|
115
|
-
resolve_classname(VALUE mod, const char *class_name, int
|
115
|
+
resolve_classname(VALUE mod, const char *class_name, int auto_define) {
|
116
116
|
VALUE clas;
|
117
117
|
ID ci = rb_intern(class_name);
|
118
118
|
|
119
|
-
if (rb_const_defined_at(mod, ci) || !
|
119
|
+
if (rb_const_defined_at(mod, ci) || !auto_define) {
|
120
120
|
clas = rb_const_get_at(mod, ci);
|
121
121
|
} else {
|
122
|
-
|
123
|
-
clas = rb_const_get_at(mod, ci); // TBD temp
|
122
|
+
clas = rb_define_class_under(mod, class_name, oj_bag_class);
|
124
123
|
}
|
125
124
|
return clas;
|
126
125
|
}
|
@@ -139,10 +138,8 @@ classname2obj(const char *name, ParseInfo pi) {
|
|
139
138
|
static VALUE
|
140
139
|
classname2class(const char *name, ParseInfo pi) {
|
141
140
|
VALUE clas;
|
142
|
-
int create = 0; // TBD from options
|
143
|
-
|
144
|
-
#if 1
|
145
141
|
VALUE *slot;
|
142
|
+
int auto_define = (Yes == pi->options->auto_define);
|
146
143
|
|
147
144
|
if (Qundef == (clas = oj_cache_get(oj_class_cache, name, &slot))) {
|
148
145
|
char class_name[1024];
|
@@ -154,7 +151,10 @@ classname2class(const char *name, ParseInfo pi) {
|
|
154
151
|
if (':' == *n) {
|
155
152
|
*s = '\0';
|
156
153
|
n++;
|
157
|
-
|
154
|
+
if (':' != *n) {
|
155
|
+
raise_error("Invalid classname, expected another ':'", pi->str, pi->s);
|
156
|
+
}
|
157
|
+
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
158
158
|
return Qundef;
|
159
159
|
}
|
160
160
|
s = class_name;
|
@@ -163,31 +163,10 @@ classname2class(const char *name, ParseInfo pi) {
|
|
163
163
|
}
|
164
164
|
}
|
165
165
|
*s = '\0';
|
166
|
-
if (Qundef != (clas = resolve_classname(clas, class_name,
|
166
|
+
if (Qundef != (clas = resolve_classname(clas, class_name, auto_define))) {
|
167
167
|
*slot = clas;
|
168
168
|
}
|
169
169
|
}
|
170
|
-
#else
|
171
|
-
char class_name[1024];
|
172
|
-
char *s;
|
173
|
-
const char *n = name;
|
174
|
-
|
175
|
-
clas = rb_cObject;
|
176
|
-
for (s = class_name; '\0' != *n; n++) {
|
177
|
-
if (':' == *n) {
|
178
|
-
*s = '\0';
|
179
|
-
n++;
|
180
|
-
if (Qundef == (clas = resolve_classname(clas, class_name, create))) {
|
181
|
-
return Qundef;
|
182
|
-
}
|
183
|
-
s = class_name;
|
184
|
-
} else {
|
185
|
-
*s++ = *n;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
*s = '\0';
|
189
|
-
clas = resolve_classname(clas, class_name, create);
|
190
|
-
#endif
|
191
170
|
return clas;
|
192
171
|
}
|
193
172
|
|
@@ -314,7 +293,11 @@ read_obj(ParseInfo pi) {
|
|
314
293
|
obj = read_next(pi, T_CLASS);
|
315
294
|
key = Qundef;
|
316
295
|
break;
|
317
|
-
case 's': //
|
296
|
+
case 's': // String
|
297
|
+
obj = read_next(pi, T_STRING);
|
298
|
+
key = Qundef;
|
299
|
+
break;
|
300
|
+
case 'm': // Symbol
|
318
301
|
obj = read_next(pi, T_SYMBOL);
|
319
302
|
key = Qundef;
|
320
303
|
break;
|
data/ext/oj/oj.c
CHANGED
@@ -54,8 +54,10 @@ ID oj_tv_nsec_id;
|
|
54
54
|
ID oj_tv_sec_id;
|
55
55
|
ID oj_tv_usec_id;
|
56
56
|
|
57
|
+
VALUE oj_bag_class;
|
57
58
|
VALUE oj_time_class;
|
58
59
|
|
60
|
+
static VALUE auto_define_sym;
|
59
61
|
static VALUE circular_sym;
|
60
62
|
static VALUE compat_sym;
|
61
63
|
static VALUE encoding_sym;
|
@@ -72,6 +74,7 @@ static struct _Options default_options = {
|
|
72
74
|
{ '\0' }, // encoding
|
73
75
|
0, // indent
|
74
76
|
No, // circular
|
77
|
+
Yes, // auto_define
|
75
78
|
ObjectMode, // mode
|
76
79
|
};
|
77
80
|
|
@@ -81,6 +84,7 @@ static struct _Options default_options = {
|
|
81
84
|
* - indent: [Fixnum] number of spaces to indent each element in an XML document
|
82
85
|
* - encoding: [String] character encoding for the JSON file
|
83
86
|
* - circular: [true|false|nil] support circular references while dumping
|
87
|
+
* - auto_define: [true|false|nil] automatically define classes if they do not exist
|
84
88
|
* - mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
|
85
89
|
* @return [Hash] all current option settings.
|
86
90
|
*/
|
@@ -92,6 +96,7 @@ get_def_opts(VALUE self) {
|
|
92
96
|
rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(default_options.encoding, elen));
|
93
97
|
rb_hash_aset(opts, indent_sym, INT2FIX(default_options.indent));
|
94
98
|
rb_hash_aset(opts, circular_sym, (Yes == default_options.circular) ? Qtrue : ((No == default_options.circular) ? Qfalse : Qnil));
|
99
|
+
rb_hash_aset(opts, auto_define_sym, (Yes == default_options.auto_define) ? Qtrue : ((No == default_options.auto_define) ? Qfalse : Qnil));
|
95
100
|
switch (default_options.mode) {
|
96
101
|
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
|
97
102
|
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
|
@@ -109,7 +114,8 @@ get_def_opts(VALUE self) {
|
|
109
114
|
* @param [Fixnum] :indent number of spaces to indent each element in an XML document
|
110
115
|
* @param [String] :encoding character encoding for the JSON file
|
111
116
|
* @param [true|false|nil] :circular support circular references while dumping
|
112
|
-
* @
|
117
|
+
* @param [true|false|nil] :auto_define automatically define classes if they do not exist
|
118
|
+
* @param [:object|:strict|:compat|:null] load and dump mode to use for JSON
|
113
119
|
* :strict raises an exception when a non-supported Object is
|
114
120
|
* encountered. :compat attempts to extract variable values from an
|
115
121
|
* Object using to_json() or to_hash() then it walks the Object's
|
@@ -122,6 +128,7 @@ static VALUE
|
|
122
128
|
set_def_opts(VALUE self, VALUE opts) {
|
123
129
|
struct _YesNoOpt ynos[] = {
|
124
130
|
{ circular_sym, &default_options.circular },
|
131
|
+
{ auto_define_sym, &default_options.auto_define },
|
125
132
|
{ Qnil, 0 }
|
126
133
|
};
|
127
134
|
YesNoOpt o;
|
@@ -366,8 +373,10 @@ void Init_oj() {
|
|
366
373
|
oj_tv_sec_id = rb_intern("tv_sec");
|
367
374
|
oj_tv_usec_id = rb_intern("tv_usec");
|
368
375
|
|
376
|
+
oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
|
369
377
|
oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
|
370
378
|
|
379
|
+
auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
|
371
380
|
circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
|
372
381
|
compat_sym = ID2SYM(rb_intern("compat")); rb_ary_push(keep, compat_sym);
|
373
382
|
encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
|
data/ext/oj/oj.h
CHANGED
@@ -78,10 +78,11 @@ typedef enum {
|
|
78
78
|
} Mode;
|
79
79
|
|
80
80
|
typedef struct _Options {
|
81
|
-
char encoding[64];
|
82
|
-
int indent;
|
83
|
-
char circular;
|
84
|
-
char
|
81
|
+
char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
|
82
|
+
int indent; // indention for dump, default 2
|
83
|
+
char circular; // YesNo
|
84
|
+
char auto_define; // YesNo
|
85
|
+
char mode; // Mode
|
85
86
|
} *Options;
|
86
87
|
|
87
88
|
extern VALUE oj_parse(char *json, Options options);
|
@@ -92,6 +93,7 @@ extern void _oj_raise_error(const char *msg, const char *xml, const char *curren
|
|
92
93
|
|
93
94
|
extern VALUE Oj;
|
94
95
|
|
96
|
+
extern VALUE oj_bag_class;
|
95
97
|
extern VALUE oj_time_class;
|
96
98
|
|
97
99
|
extern ID oj_at_id;
|
data/lib/oj.rb
CHANGED
data/lib/oj/bag.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
module Oj
|
3
|
+
|
4
|
+
# A generic class that is used only for storing attributes. It is the base
|
5
|
+
# Class for auto-generated classes in the storage system. Instance variables
|
6
|
+
# are added using the instance_variable_set() method. All instance variables
|
7
|
+
# can be accessed using the variable name (without the @ prefix). No setters
|
8
|
+
# are provided as the Class is intended for reading only.
|
9
|
+
class Bag
|
10
|
+
|
11
|
+
# The initializer can take multiple arguments in the form of key values
|
12
|
+
# where the key is the variable name and the value is the variable
|
13
|
+
# value. This is intended for testing purposes only.
|
14
|
+
# @example Oj::Bag.new(:@x => 42, :@y => 57)
|
15
|
+
# @param [Hash] args instance variable symbols and their values
|
16
|
+
def initialize(args={ })
|
17
|
+
args.each do |k,v|
|
18
|
+
self.instance_variable_set(k, v)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Replaces the Object.respond_to?() method.
|
23
|
+
# @param [Symbol] m method symbol
|
24
|
+
# @return [Boolean] true for any method that matches an instance
|
25
|
+
# variable reader, otherwise false.
|
26
|
+
def respond_to?(m)
|
27
|
+
return true if super
|
28
|
+
at_m = ('@' + m.to_s).to_sym
|
29
|
+
instance_variables.include?(at_m)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Handles requests for variable values. Others cause an Exception to be
|
33
|
+
# raised.
|
34
|
+
# @param [Symbol] m method symbol
|
35
|
+
# @return [Boolean] the value of the specified instance variable.
|
36
|
+
# @raise [ArgumentError] if an argument is given. Zero arguments expected.
|
37
|
+
# @raise [NoMethodError] if the instance variable is not defined.
|
38
|
+
def method_missing(m, *args, &block)
|
39
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 0) to method #{m}") unless args.nil? or args.empty?
|
40
|
+
at_m = ('@' + m.to_s).to_sym
|
41
|
+
raise NoMethodError.new("undefined method #{m}", m) unless instance_variable_defined?(at_m)
|
42
|
+
instance_variable_get(at_m)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Replaces eql?() with something more reasonable for this Class.
|
46
|
+
# @param [Object] other Object to compare self to
|
47
|
+
# @return [Boolean] true if each variable and value are the same, otherwise false.
|
48
|
+
def eql?(other)
|
49
|
+
return false if (other.nil? or self.class != other.class)
|
50
|
+
ova = other.instance_variables
|
51
|
+
iv = instance_variables
|
52
|
+
return false if ova.size != iv.size
|
53
|
+
iv.each do |vid|
|
54
|
+
return false if instance_variable_get(vid) != other.instance_variable_get(vid)
|
55
|
+
end
|
56
|
+
true
|
57
|
+
end
|
58
|
+
alias == eql?
|
59
|
+
|
60
|
+
# Define a new class based on the Oj::Bag class. This is used internally in
|
61
|
+
# the Oj module and is available to service wrappers that receive XML
|
62
|
+
# requests that include Objects of Classes not defined in the storage
|
63
|
+
# process.
|
64
|
+
# @param [String] classname Class name or symbol that includes Module names.
|
65
|
+
# @return [Object] an instance of the specified Class.
|
66
|
+
# @raise [NameError] if the classname is invalid.
|
67
|
+
def self.define_class(classname)
|
68
|
+
classname = classname.to_s unless classname.is_a?(String)
|
69
|
+
tokens = classname.split('::').map { |n| n.to_sym }
|
70
|
+
raise NameError.new("Invalid classname '#{classname}") if tokens.empty?
|
71
|
+
m = Object
|
72
|
+
tokens[0..-2].each do |sym|
|
73
|
+
if m.const_defined?(sym)
|
74
|
+
m = m.const_get(sym)
|
75
|
+
else
|
76
|
+
c = Module.new
|
77
|
+
m.const_set(sym, c)
|
78
|
+
m = c
|
79
|
+
end
|
80
|
+
end
|
81
|
+
sym = tokens[-1]
|
82
|
+
if m.const_defined?(sym)
|
83
|
+
c = m.const_get(sym)
|
84
|
+
else
|
85
|
+
c = Class.new(Oj::Bag)
|
86
|
+
m.const_set(sym, c)
|
87
|
+
end
|
88
|
+
c
|
89
|
+
end
|
90
|
+
|
91
|
+
end # Bag
|
92
|
+
end # Oj
|
data/lib/oj/version.rb
CHANGED
data/test/tests.rb
CHANGED
@@ -50,6 +50,7 @@ class Juice < ::Test::Unit::TestCase
|
|
50
50
|
:encoding=>nil,
|
51
51
|
:indent=>0,
|
52
52
|
:circular=>false,
|
53
|
+
:auto_define=>true,
|
53
54
|
:mode=>:object})
|
54
55
|
end
|
55
56
|
|
@@ -58,11 +59,13 @@ class Juice < ::Test::Unit::TestCase
|
|
58
59
|
:encoding=>nil,
|
59
60
|
:indent=>0,
|
60
61
|
:circular=>false,
|
62
|
+
:auto_define=>true,
|
61
63
|
:mode=>:object}
|
62
64
|
o2 = {
|
63
65
|
:encoding=>"UTF-8",
|
64
66
|
:indent=>4,
|
65
67
|
:circular=>true,
|
68
|
+
:auto_define=>false,
|
66
69
|
:mode=>:compat}
|
67
70
|
o3 = { :indent => 4 }
|
68
71
|
Oj.default_options = o2
|
@@ -109,6 +112,11 @@ class Juice < ::Test::Unit::TestCase
|
|
109
112
|
dump_and_load("a\u0041", false)
|
110
113
|
end
|
111
114
|
|
115
|
+
def test_string_object
|
116
|
+
dump_and_load('abc', false)
|
117
|
+
dump_and_load(':abc', false)
|
118
|
+
end
|
119
|
+
|
112
120
|
def test_encode
|
113
121
|
Oj.default_options = { :encoding => 'UTF-8' }
|
114
122
|
dump_and_load("ぴーたー", false)
|
@@ -348,6 +356,17 @@ class Juice < ::Test::Unit::TestCase
|
|
348
356
|
assert_equal(e, e2);
|
349
357
|
end
|
350
358
|
|
359
|
+
def test_bag
|
360
|
+
json = %{{
|
361
|
+
"^o":"Jem",
|
362
|
+
"x":true,
|
363
|
+
"y":58 }}
|
364
|
+
obj = Oj.load(json, :mode => :object)
|
365
|
+
assert_equal('Jem', obj.class.name)
|
366
|
+
assert_equal(true, obj.x)
|
367
|
+
assert_equal(58, obj.y)
|
368
|
+
end
|
369
|
+
|
351
370
|
def dump_and_load(obj, trace=false)
|
352
371
|
json = Oj.dump(obj, :indent => 2)
|
353
372
|
puts json if trace
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'The fastest JSON parser and object serializer. '
|
15
15
|
email: peter@ohler.com
|
@@ -19,6 +19,7 @@ extensions:
|
|
19
19
|
extra_rdoc_files:
|
20
20
|
- README.md
|
21
21
|
files:
|
22
|
+
- lib/oj/bag.rb
|
22
23
|
- lib/oj/version.rb
|
23
24
|
- lib/oj.rb
|
24
25
|
- ext/oj/extconf.rb
|