oj 0.8.0 → 0.9.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.

@@ -45,8 +45,10 @@ void Init_oj();
45
45
 
46
46
  VALUE Oj = Qnil;
47
47
 
48
+ ID oj_as_json_id;
48
49
  ID oj_at_id;
49
50
  ID oj_instance_variables_id;
51
+ ID oj_json_create_id;
50
52
  ID oj_to_hash_id;
51
53
  ID oj_to_json_id;
52
54
  ID oj_to_sym_id;
@@ -55,6 +57,7 @@ ID oj_tv_sec_id;
55
57
  ID oj_tv_usec_id;
56
58
 
57
59
  VALUE oj_bag_class;
60
+ VALUE oj_struct_class;
58
61
  VALUE oj_time_class;
59
62
 
60
63
  static VALUE auto_define_sym;
@@ -188,6 +191,7 @@ static void
188
191
  parse_options(VALUE ropts, Options copts) {
189
192
  struct _YesNoOpt ynos[] = {
190
193
  { circular_sym, &copts->circular },
194
+ { auto_define_sym, &copts->auto_define },
191
195
  { Qnil, 0 }
192
196
  };
193
197
  YesNoOpt o;
@@ -364,8 +368,10 @@ void Init_oj() {
364
368
  rb_define_module_function(Oj, "dump", dump, -1);
365
369
  rb_define_module_function(Oj, "to_file", to_file, -1);
366
370
 
371
+ oj_as_json_id = rb_intern("as_json");
367
372
  oj_at_id = rb_intern("at");
368
373
  oj_instance_variables_id = rb_intern("instance_variables");
374
+ oj_json_create_id = rb_intern("json_create");
369
375
  oj_to_hash_id = rb_intern("to_hash");
370
376
  oj_to_json_id = rb_intern("to_json");
371
377
  oj_to_sym_id = rb_intern("to_sym");
@@ -374,6 +380,7 @@ void Init_oj() {
374
380
  oj_tv_usec_id = rb_intern("tv_usec");
375
381
 
376
382
  oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
383
+ oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
377
384
  oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
378
385
 
379
386
  auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_ary_push(keep, auto_define_sym);
@@ -94,10 +94,13 @@ extern void _oj_raise_error(const char *msg, const char *xml, const char *curren
94
94
  extern VALUE Oj;
95
95
 
96
96
  extern VALUE oj_bag_class;
97
+ extern VALUE oj_struct_class;
97
98
  extern VALUE oj_time_class;
98
99
 
100
+ extern ID oj_as_json_id;
99
101
  extern ID oj_at_id;
100
102
  extern ID oj_instance_variables_id;
103
+ extern ID oj_json_create_id;
101
104
  extern ID oj_to_hash_id;
102
105
  extern ID oj_to_json_id;
103
106
  extern ID oj_to_sym_id;
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '0.8.0'
4
+ VERSION = '0.9.0'
5
5
  end
@@ -63,8 +63,8 @@ end
63
63
  if files.empty?
64
64
  $obj = do_sample ? sample_doc(2) : files('..')
65
65
  $mars = Marshal.dump($obj)
66
- $xml = Ox.dump($obj, :indent => $indent, circular: $circular)
67
- $json = Oj.dump($obj, :indent => $indent, circular: $circular)
66
+ $xml = Ox.dump($obj, :indent => $indent, :circular => $circular)
67
+ $json = Oj.dump($obj, :indent => $indent, :circular => $circular)
68
68
  File.open('sample.xml', 'w') { |f| f.write($xml) }
69
69
  File.open('sample.json', 'w') { |f| f.write($json) }
70
70
  File.open('sample.marshal', 'w') { |f| f.write($mars) }
@@ -79,7 +79,7 @@ else
79
79
  end
80
80
  end
81
81
 
82
- Oj.default_options = { :mode => :object, :indent => $indent }
82
+ Oj.default_options = { :mode => :object, :indent => $indent, :circular => $circular }
83
83
 
84
84
  if do_load
85
85
  puts '-' * 80
@@ -16,36 +16,54 @@ require 'oj'
16
16
  require 'ox'
17
17
 
18
18
  class Jazz
19
+ attr_accessor :boolean, :number, :string
20
+
19
21
  def initialize()
20
22
  @boolean = true
21
23
  @number = 58
22
24
  @string = "A string"
23
- @array = [true, false, nil]
24
- @hash = { 'one' => 1, 'two' => 2 }
25
25
  end
26
+
27
+ def eql?(o)
28
+ (self.class == o.class &&
29
+ boolean == o.boolean &&
30
+ number == o.number &&
31
+ string == o.string)
32
+ end
33
+ alias == eql?
34
+
26
35
  def to_json(*) # Yajl and JSON have different signatures
27
36
  %{
28
- { "boolean":#{@boolean},
29
- "number":#{@number},
30
- "string":#{@string},
31
- "array":#{@array},
32
- "hash":#{@hash},
33
- }
34
- }
37
+ { "json_class":"Jazz",
38
+ "boolean":#{@boolean},
39
+ "number":#{@number},
40
+ "string":"#{@string}"
41
+ }}
35
42
  end
43
+
36
44
  def to_hash()
37
- { 'boolean' => @boolean,
45
+ { 'json_class' => "Jazz",
46
+ 'boolean' => @boolean,
38
47
  'number' => @number,
39
- 'string' => @string,
40
- 'array' => @array,
41
- 'hash' => @hash,
48
+ 'string' => @string
42
49
  }
43
50
  end
51
+ alias as_json to_hash
52
+
44
53
  def to_msgpack(out)
45
54
  out << MessagePack.pack(to_hash())
46
55
  end
56
+
57
+ def self.json_create(h)
58
+ j = self.new()
59
+ j.instance_variable_set(:@boolean, h['boolean'])
60
+ j.instance_variable_set(:@number, h['number'])
61
+ j.instance_variable_set(:@string, h['string'])
62
+ j
63
+ end
47
64
  end
48
65
 
66
+ $verbose = false
49
67
  $indent = 0
50
68
  $iter = 100000
51
69
  $with_object = true
@@ -53,6 +71,7 @@ $with_bignum = true
53
71
  $with_nums = true
54
72
 
55
73
  opts = OptionParser.new
74
+ opts.on("-v", "verbose") { $verbose = true }
56
75
  opts.on("-c", "--count [Int]", Integer, "iterations") { |i| $iter = i }
57
76
  opts.on("-i", "--indent [Int]", Integer, "indentation") { |i| $indent = i }
58
77
  opts.on("-o", "without objects") { $with_object = false }
@@ -70,7 +89,6 @@ if $with_nums
70
89
  'e' => { 'one' => 1, 'two' => 2 },
71
90
  'f' => nil,
72
91
  }
73
- $obj['g'] = Jazz.new() if $with_object
74
92
  $obj['h'] = 12345678901234567890123456789 if $with_bignum
75
93
  else
76
94
  $obj = {
@@ -82,50 +100,102 @@ else
82
100
  'f' => nil,
83
101
  }
84
102
  end
103
+ $obj['g'] = Jazz.new() if $with_object
85
104
 
86
105
  Oj.default_options = { :indent => $indent, :mode => :compat }
87
106
  Ox.default_options = { :indent => $indent, :mode => :object }
88
107
 
89
108
  $json = Oj.dump($obj)
109
+ $obj_json = Oj.dump($obj, :mode => :object)
90
110
  $xml = Ox.dump($obj, :indent => $indent)
111
+ $failed = {} # key is same as String used in tests later
112
+
113
+ def capture_error(tag, orig, load_key, dump_key, &blk)
114
+ begin
115
+ obj = blk.call(orig)
116
+ raise "#{tag} #{dump_key} and #{load_key} did not return the same object as the original." unless orig == obj
117
+ rescue Exception => e
118
+ $failed[tag] = "#{e.class}: #{e.message}"
119
+ end
120
+ end
121
+
122
+ # Verify that all packages dump and load correctly and return the same Object as the original.
123
+ capture_error('Oj:compat', $obj, 'load', 'dump') { |o| Oj.load(Oj.dump(o)) }
124
+ capture_error('Oj', $obj, 'load', 'dump') { |o| Oj.load(Oj.dump(o, :mode => :compat), :mode => :compat) }
125
+ capture_error('Ox', $obj, 'load', 'dump') { |o| Ox.load(Ox.dump(o, :mode => :object), :mode => :object) }
126
+ capture_error('MessagePack', $obj, 'unpack', 'pack') { |o| MessagePack.unpack(MessagePack.pack($obj)) }
127
+ capture_error('Yajl', $obj, 'encode', 'parse') { |o| Yajl::Parser.parse(Yajl::Encoder.encode(o)) }
128
+ capture_error('JSON::Ext', $obj, 'generate', 'parse') { |o| JSON.generator = JSON::Ext::Generator; JSON::Ext::Parser.new(JSON.generate(o)).parse }
129
+ capture_error('JSON::Pure', $obj, 'generate', 'parse') { |o| JSON.generator = JSON::Pure::Generator; JSON::Pure::Parser.new(JSON.generate(o)).parse }
130
+
91
131
  begin
92
132
  $msgpack = MessagePack.pack($obj)
93
133
  rescue Exception => e
94
- puts "MessagePack failed to pack! #{e.class}: #{e.message}.\nSkipping."
95
134
  $msgpack = nil
96
135
  end
97
136
 
137
+ if $verbose
138
+ puts "json:\n#{$json}\n"
139
+ puts "object json:\n#{$obj_json}\n"
140
+ puts "Oj loaded object:\n#{Oj.load($json)}\n"
141
+ puts "Yajl loaded object:\n#{Yajl::Parser.parse($json)}\n"
142
+ puts "JSON loaded object:\n#{JSON::Ext::Parser.new($json).parse}\n"
143
+ end
144
+
98
145
  puts '-' * 80
99
146
  puts "Load/Parse Performance"
100
147
  perf = Perf.new()
101
- perf.add('Oj', 'load') { Oj.load($json) }
102
- perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) }
103
- perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse }
104
- perf.add('JSON::Pure', 'parse') { JSON::Pure::Parser.new($json).parse }
105
- perf.add('Ox', 'load') { Ox.load($xml) }
106
- perf.add('MessagePack', 'unpack') { MessagePack.unpack($msgpack) } unless $msgpack.nil?
148
+ unless $failed.has_key?('Oj:compat')
149
+ perf.add('Oj:compat', 'load') { Oj.load($json) }
150
+ perf.before('Oj:compat') { Oj.default_options = { :mode => :compat} }
151
+ end
152
+ unless $failed.has_key?('Oj')
153
+ perf.add('Oj', 'load') { Oj.load($obj_json) }
154
+ perf.before('Oj') { Oj.default_options = { :mode => :object} }
155
+ end
156
+ perf.add('Yajl', 'parse') { Yajl::Parser.parse($json) } unless $failed.has_key?('Yajl')
157
+ perf.add('JSON::Ext', 'parse') { JSON::Ext::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
158
+ perf.add('JSON::Pure', 'parse') { JSON::Pure::Parser.new($json).parse } unless $failed.has_key?('JSON::Ext')
159
+ perf.add('Ox', 'load') { Ox.load($xml) } unless $failed.has_key?('Ox')
160
+ perf.add('MessagePack', 'unpack') { MessagePack.unpack($msgpack) } unless $failed.has_key?('MessagePack')
107
161
  perf.run($iter)
108
162
 
109
163
  puts
110
164
  puts '-' * 80
111
165
  puts "Dump/Encode/Generate Performance"
112
166
  perf = Perf.new()
113
- perf.add('Oj', 'dump') { Oj.dump($obj) }
114
- perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) }
115
- if 0 == $indent
116
- perf.add('JSON::Ext', 'generate') { JSON.generate($obj) }
117
- else
118
- perf.add('JSON::Ext', 'generate') { JSON.pretty_generate($obj) }
167
+ unless $failed.has_key?('Oj:compat')
168
+ perf.add('Oj:compat', 'dump') { Oj.dump($obj) }
169
+ perf.before('Oj:compat') { Oj.default_options = { :mode => :compat} }
119
170
  end
120
- perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
121
- if 0 == $indent
122
- perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
123
- else
124
- perf.add('JSON::Pure', 'generate') { JSON.pretty_generate($obj) }
171
+ unless $failed.has_key?('Oj')
172
+ perf.add('Oj', 'dump') { Oj.dump($obj) }
173
+ perf.before('Oj') { Oj.default_options = { :mode => :object} }
125
174
  end
126
- perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
127
- perf.add('Ox', 'dump') { Ox.dump($obj) }
128
- perf.add('MessagePack', 'pack') { MessagePack.pack($obj) } unless $msgpack.nil?
175
+ perf.add('Yajl', 'encode') { Yajl::Encoder.encode($obj) } unless $failed.has_key?('Yajl')
176
+ unless $failed.has_key?('JSON::Ext')
177
+ if 0 == $indent
178
+ perf.add('JSON::Ext', 'generate') { JSON.generate($obj) }
179
+ else
180
+ perf.add('JSON::Ext', 'generate') { JSON.pretty_generate($obj) }
181
+ end
182
+ perf.before('JSON::Ext') { JSON.generator = JSON::Ext::Generator }
183
+ end
184
+ unless $failed.has_key?('JSON::Pure')
185
+ if 0 == $indent
186
+ perf.add('JSON::Pure', 'generate') { JSON.generate($obj) }
187
+ else
188
+ perf.add('JSON::Pure', 'generate') { JSON.pretty_generate($obj) }
189
+ end
190
+ perf.before('JSON::Pure') { JSON.generator = JSON::Pure::Generator }
191
+ end
192
+ perf.add('Ox', 'dump') { Ox.dump($obj) } unless $failed.has_key?('Ox')
193
+ perf.add('MessagePack', 'pack') { MessagePack.pack($obj) } unless $failed.has_key?('MessagePack')
129
194
  perf.run($iter)
130
195
 
131
196
  puts
197
+
198
+ unless $failed.empty?
199
+ puts "The following packages were not included for the reason listed"
200
+ $failed.each { |tag,msg| puts "***** #{tag}: #{msg}" }
201
+ end
@@ -8,7 +8,7 @@ require 'test/unit'
8
8
  require 'oj'
9
9
 
10
10
  class Jam
11
- attr_reader :x, :y
11
+ attr_accessor :x, :y
12
12
 
13
13
  def initialize(x, y)
14
14
  @x = x
@@ -28,9 +28,12 @@ class Jeez < Jam
28
28
  end
29
29
 
30
30
  def to_json()
31
- %{{"x":#{@x},"y":#{@y}}}
31
+ %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
32
+ end
33
+
34
+ def self.json_create(h)
35
+ self.new(h['x'], h['y'])
32
36
  end
33
-
34
37
  end # Jeez
35
38
 
36
39
  class Jazz < Jam
@@ -38,10 +41,19 @@ class Jazz < Jam
38
41
  super
39
42
  end
40
43
  def to_hash()
41
- { 'x' => @x, 'y' => @y }
44
+ { 'json_class' => self.class.to_s, 'x' => @x, 'y' => @y }
45
+ end
46
+ def self.json_create(h)
47
+ self.new(h['x'], h['y'])
42
48
  end
43
49
  end # Jazz
44
50
 
51
+ class Range
52
+ def to_hash()
53
+ { 'begin' => self.begin, 'end' => self.end, 'exclude_end' => self.exclude_end? }
54
+ end
55
+ end # Range
56
+
45
57
  class Juice < ::Test::Unit::TestCase
46
58
 
47
59
  def test_get_options
@@ -260,9 +272,11 @@ class Juice < ::Test::Unit::TestCase
260
272
  assert_equal('null', json)
261
273
  end
262
274
  def test_json_object_compat
275
+ Oj.default_options = { :mode => :compat }
263
276
  obj = Jeez.new(true, 58)
264
- json = Oj.dump(obj, :mode => :compat, :indent => 2)
265
- assert_equal(%{{"x":true,"y":58}}, json)
277
+ json = Oj.dump(obj, :indent => 2)
278
+ assert_equal(%{{"json_class":"Jeez","x":true,"y":58}}, json)
279
+ dump_and_load(obj, false)
266
280
  end
267
281
  def test_json_object_object
268
282
  obj = Jeez.new(true, 58)
@@ -293,6 +307,7 @@ class Juice < ::Test::Unit::TestCase
293
307
  obj = Jazz.new(true, 58)
294
308
  json = Oj.dump(obj, :mode => :compat, :indent => 2)
295
309
  assert_equal(%{{
310
+ "json_class":"Jazz",
296
311
  "x":true,
297
312
  "y":58}}, json)
298
313
  end
@@ -307,7 +322,7 @@ class Juice < ::Test::Unit::TestCase
307
322
  assert_equal(obj, obj2)
308
323
  end
309
324
 
310
- # Object without to_json() or to_hash()
325
+ # Object without to_json() or to_hash()
311
326
  def test_object_strict
312
327
  obj = Jam.new(true, 58)
313
328
  begin
@@ -339,6 +354,7 @@ class Juice < ::Test::Unit::TestCase
339
354
  assert_equal(obj, obj2)
340
355
  end
341
356
 
357
+ # Exception
342
358
  def test_exception
343
359
  err = nil
344
360
  begin
@@ -356,6 +372,34 @@ class Juice < ::Test::Unit::TestCase
356
372
  assert_equal(e, e2);
357
373
  end
358
374
 
375
+ # Range
376
+ def test_range_strict
377
+ begin
378
+ json = Oj.dump(1..7, :mode => :strict)
379
+ rescue Exception => e
380
+ assert(true)
381
+ end
382
+ end
383
+ def test_range_null
384
+ json = Oj.dump(1..7, :mode => :null)
385
+ assert_equal('null', json)
386
+ end
387
+ def test_range_compat
388
+ json = Oj.dump(1..7, :mode => :compat)
389
+ assert_equal(%{{"begin":1,"end":7,"exclude_end":false}}, json)
390
+ json = Oj.dump(1...7, :mode => :compat)
391
+ assert_equal(%{{"begin":1,"end":7,"exclude_end":true}}, json)
392
+ end
393
+ def test_range_object
394
+ Oj.default_options = { :mode => :object }
395
+ json = Oj.dump(1..7, :mode => :object, :indent => 0)
396
+ assert_equal(%{{"^u":["Range",1,7,false]}}, json)
397
+ dump_and_load(1..7, false)
398
+ dump_and_load(1..1, false)
399
+ dump_and_load(1...7, false)
400
+ end
401
+
402
+ # autodefine Oj::Bag
359
403
  def test_bag
360
404
  json = %{{
361
405
  "^o":"Jem",
@@ -367,6 +411,63 @@ class Juice < ::Test::Unit::TestCase
367
411
  assert_equal(58, obj.y)
368
412
  end
369
413
 
414
+ # Circular
415
+ def test_circular_object
416
+ obj = Jam.new(nil, 58)
417
+ obj.x = obj
418
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
419
+ assert_equal(%{{
420
+ "^o":"Jam",
421
+ "^i":1,
422
+ "x":"^r1",
423
+ "y":58}}, json)
424
+ obj2 = Oj.load(json, :mode => :object, :circular => true)
425
+ assert_equal(obj2.x.__id__, obj2.__id__)
426
+ end
427
+
428
+ def test_circular_hash
429
+ h = { 'a' => 7 }
430
+ h['b'] = h
431
+ json = Oj.dump(h, :mode => :object, :indent => 2, :circular => true)
432
+ assert_equal(%{{
433
+ "^i":1,
434
+ "a":7,
435
+ "b":"^r1"}}, json)
436
+ h2 = Oj.load(json, :mode => :object, :circular => true)
437
+ assert_equal(h['b'].__id__, h.__id__)
438
+ end
439
+
440
+ def test_circular_array
441
+ a = [7]
442
+ a << a
443
+ json = Oj.dump(a, :mode => :object, :indent => 2, :circular => true)
444
+ assert_equal(%{[
445
+ "^i1",
446
+ 7,
447
+ "^r1"]}, json)
448
+ a2 = Oj.load(json, :mode => :object, :circular => true)
449
+ assert_equal(a2[1].__id__, a2.__id__)
450
+ end
451
+
452
+ def test_circular
453
+ h = { 'a' => 7 }
454
+ obj = Jam.new(h, 58)
455
+ obj.x['b'] = obj
456
+ json = Oj.dump(obj, :mode => :object, :indent => 2, :circular => true)
457
+ assert_equal(%{{
458
+ "^o":"Jam",
459
+ "^i":1,
460
+ "x":{
461
+ "^i":2,
462
+ "a":7,
463
+ "b":"^r1"
464
+ },
465
+ "y":58}}, json)
466
+ obj2 = Oj.load(json, :mode => :object, :circular => true)
467
+ assert_equal(obj.x.__id__, h.__id__)
468
+ assert_equal(h['b'].__id__, obj.__id__)
469
+ end
470
+
370
471
  def dump_and_load(obj, trace=false)
371
472
  json = Oj.dump(obj, :indent => 2)
372
473
  puts json if trace