# Oj gem A fast JSON parser and Object marshaller as a Ruby gem. ## Installation gem install oj ## Source *GitHub* *repo*: https://github.com/ohler55/oj *RubyGems* *repo*: https://rubygems.org/gems/oj ## Build Status [![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj) ## Links of Interest *Fast XML parser and marshaller on RubyGems*: https://rubygems.org/gems/ox *Fast XML parser and marshaller on GitHub*: https://rubygems.org/gems/ox ## Release Notes ### Release 0.8.0 - Auto creation of data classes when unmarshalling Objects if the Class is not defined ## Description Optimized JSON (Oj), as the name implies was written to provide speed optimized JSON handling. It was designed as a faster alternative to Yajl and other the common Ruby JSON parsers. So far is has achieved that at about 2 time faster than Yajl for parsing and 3 or more times faster writing JSON. Oj has several dump or serialization modes which control how Objects are converted to JSON. These modes are set with the :effort option in either the default options or as one of the options to the dump() method. - :strict mode will only allow the 7 basic JSON types to be serialized. Any other Object will raise and Exception. - :null mode replaces any Object that is not one of the JSON types is replaced by a JSON null. - :object mode will dump any Object as a JSON Object with keys that match the Ruby Object's variable names without the '@' character. This is the highest performance mode. - :compat mode is is the compatible with other systems. It will serialize any Object but will check to see if the Object implements a to_hash() or to_json() method. If either exists that method is used for serializing the Object. The to_hash() is more flexible and produces more consistent output so it has a preference over the to_json() method. If neither the to_json() or to_hash() methods exist then the Oj internal Object variable encoding is used. Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, and RBX. ## Planned Releases - Release 0.9: Support for circular references. - Release 1.0: A JSON stream parser. ## Comparisons The following table shows the difference is speeds between several serialization packages. The tests had to be scaled back due to limitation of some of the gems. I finally gave up trying to get JSON Pure to serialize without errors with Ruby 1.9.3. It had internal errors on anything other than a simple JSON structure. The errors encountered were: - MessagePack fails to convert Bignum to JSON - JSON Pure fails to serialize any numbers or Objects with the to_json() method Options were added to the test/perf_strict.rb test to run the test without Object encoding and without Bignums. None of the packages except Oj were able to serialize Ruby Objects that did not have a to_json() method or were of the 7 native JSON types. A perf_obj.rb file was added for comparing different Object marshalling packages. It is also worth noting that although Oj is slightly behind MessagePack for parsing, Oj serialization is much faster than MessagePack even though Oj uses human readable JSON vs the binary MessagePack format. The results: with Object and Bignum encoding: MessagePack failed to pack! RangeError: bignum too big to convert into `unsigned long long'. Skipping. -------------------------------------------------------------------------------- Load/Parse Performance Oj.load 100000 times in 1.384 seconds or 72230.276 load/sec. Yajl.parse 100000 times in 2.475 seconds or 40401.331 parse/sec. JSON::Ext.parse 100000 times in 2.562 seconds or 39037.263 parse/sec. JSON::Pure.parse 100000 times in 20.914 seconds or 4781.518 parse/sec. Ox.load 100000 times in 1.517 seconds or 65923.576 load/sec. Summary: System time (secs) rate (ops/sec) ---------- ----------- -------------- Oj 1.384 72230.276 Ox 1.517 65923.576 Yajl 2.475 40401.331 JSON::Ext 2.562 39037.263 JSON::Pure 20.914 4781.518 Comparison Matrix (performance factor, 2.0 row is means twice as fast as column) Oj Ox Yajl JSON::Ext JSON::Pure ---------- ---------- ---------- ---------- ---------- ---------- Oj 1.00 1.10 1.79 1.85 15.11 Ox 0.91 1.00 1.63 1.69 13.79 Yajl 0.56 0.61 1.00 1.03 8.45 JSON::Ext 0.54 0.59 0.97 1.00 8.16 JSON::Pure 0.07 0.07 0.12 0.12 1.00 -------------------------------------------------------------------------------- Dump/Encode/Generate Performance Oj.dump 100000 times in 0.819 seconds or 122096.842 dump/sec. Yajl.encode 100000 times in 2.221 seconds or 45014.913 encode/sec. JSON::Ext.generate 100000 times in 5.082 seconds or 19678.462 generate/sec. ***** JSON::Pure.generate failed! TypeError: wrong argument type JSON::Pure::Generator::State (expected Data) Ox.dump 100000 times in 0.532 seconds or 188014.455 dump/sec. Summary: System time (secs) rate (ops/sec) --------- ----------- -------------- Ox 0.532 188014.455 Oj 0.819 122096.842 Yajl 2.221 45014.913 JSON::Ext 5.082 19678.462 Comparison Matrix (performance factor, 2.0 row is means twice as fast as column) Ox Oj Yajl JSON::Ext --------- --------- --------- --------- --------- Ox 1.00 1.54 4.18 9.55 Oj 0.65 1.00 2.71 6.20 Yajl 0.24 0.37 1.00 2.29 JSON::Ext 0.10 0.16 0.44 1.00 without Objects or numbers (for JSON Pure) JSON: -------------------------------------------------------------------------------- Load/Parse Performance Oj.load 100000 times in 0.737 seconds or 135683.185 load/sec. Yajl.parse 100000 times in 1.352 seconds or 73978.778 parse/sec. JSON::Ext.parse 100000 times in 1.433 seconds or 69780.554 parse/sec. JSON::Pure.parse 100000 times in 12.974 seconds or 7707.624 parse/sec. Ox.load 100000 times in 0.904 seconds or 110596.591 load/sec. MessagePack.unpack 100000 times in 0.644 seconds or 155281.191 unpack/sec. Summary: System time (secs) rate (ops/sec) ----------- ----------- -------------- MessagePack 0.644 155281.191 Oj 0.737 135683.185 Ox 0.904 110596.591 Yajl 1.352 73978.778 JSON::Ext 1.433 69780.554 JSON::Pure 12.974 7707.624 Comparison Matrix (performance factor, 2.0 row is means twice as fast as column) MessagePack Oj Ox Yajl JSON::Ext JSON::Pure ----------- ----------- ----------- ----------- ----------- ----------- ----------- MessagePack 1.00 1.14 1.40 2.10 2.23 20.15 Oj 0.87 1.00 1.23 1.83 1.94 17.60 Ox 0.71 0.82 1.00 1.49 1.58 14.35 Yajl 0.48 0.55 0.67 1.00 1.06 9.60 JSON::Ext 0.45 0.51 0.63 0.94 1.00 9.05 JSON::Pure 0.05 0.06 0.07 0.10 0.11 1.00 -------------------------------------------------------------------------------- Dump/Encode/Generate Performance Oj.dump 100000 times in 0.161 seconds or 620058.906 dump/sec. Yajl.encode 100000 times in 0.765 seconds or 130637.498 encode/sec. JSON::Ext.generate 100000 times in 3.306 seconds or 30250.212 generate/sec. JSON::Pure.generate 100000 times in 7.067 seconds or 14150.026 generate/sec. Ox.dump 100000 times in 0.178 seconds or 561312.123 dump/sec. MessagePack.pack 100000 times in 0.306 seconds or 326301.535 pack/sec. Summary: System time (secs) rate (ops/sec) ----------- ----------- -------------- Oj 0.161 620058.906 Ox 0.178 561312.123 MessagePack 0.306 326301.535 Yajl 0.765 130637.498 JSON::Ext 3.306 30250.212 JSON::Pure 7.067 14150.026 Comparison Matrix (performance factor, 2.0 row is means twice as fast as column) Oj Ox MessagePack Yajl JSON::Ext JSON::Pure ----------- ----------- ----------- ----------- ----------- ----------- ----------- Oj 1.00 1.10 1.90 4.75 20.50 43.82 Ox 0.91 1.00 1.72 4.30 18.56 39.67 MessagePack 0.53 0.58 1.00 2.50 10.79 23.06 Yajl 0.21 0.23 0.40 1.00 4.32 9.23 JSON::Ext 0.05 0.05 0.09 0.23 1.00 2.14 JSON::Pure 0.02 0.03 0.04 0.11 0.47 1.00 ### Simple JSON Writing and Parsing: require 'oj' h = { 'one' => 1, 'array' => [ true, false ] } json = Oj.dump(h) # json = # { # "one":1, # "array":[ # true, # false # ] # } h2 = Oj.parse(json) puts "Same? #{h == h2}" # true ### Object JSON format: In :object mode Oj generates JSON that follows conventions which allow Class and other information such as Object IDs for circular reference detection. The formating follows the following rules. 1. JSON native types, true, false, nil, String, Hash, Array, and Number are encoded normally. 2. If a Hash uses Symbols as keys those keys appear as Strings with a leading ':' character. 3. The '^' character denotes a special key value when in a JSON Object sequence. 4. If a String begins with a ':' character such as ':abc' it is encoded as {"^s":":abc"}. 5. If a Symbol begins with a ':' character such as :":abc" is is encoded as {"^m":":abc"}. 6. A "^c" JSON Object key indicates the value should be converted to a Ruby class. The sequence {"^c":"Oj::Bag"} is read as the Oj::Bag class. 7. A "^t" JSON Object key indicates the value should be converted to a Ruby Time. The sequence {"^t":1325775487.000000} is read as Jan 5, 2012 at 23:58:07. 8. A "^o" JSON Object key indicates the value should be converted to a Ruby Object. The first entry in the JSON Object must be a class with the "^o" key. After that each entry is treated as a variable of the Object where the key is the variable name without the preceeding '@'. An example is {"^o":"Oj::Bag","x":58,"y":"marbles"}. 9. When encoding an Object, if the variable name does not begin with an '@' character then the name preceeded by a '~' character. This occurs in the Exception class. An example is {"^o":"StandardError","~mesg":"A Message","~bt":[".\/tests.rb:345:in `test_exception'"]} 10. If a Hash entry has a key that is not a String or Symbol then the entry is encoded with a key of the form "^#n" where n is a hex number. The value that is an Array where the first element is the key in the Hash and the second is the value. An example is {"^#3":[2,5]}. 11. A "^i" JSON entry in either an Object or Array is the ID of the Ruby Object being encoded. It is used when the :circular flag is set. It can appear in either a JSON Object or in a JSON Array. If alone it represented a link to the original Hash or JSON. If an added attribute it is the ID of the original Object or Array. Examples are TBD.