Let’s talk about serializing objects in Ruby today.
Built-In Serialization Mechanisms
Ruby has two object serialization mechanisms built into the lauguage. One is what we are very familiar of, YAML(YAML Ain’t Markup Language), which is also human readable format, and the other one is binary format.
In Ruby, any objects can be serialized into YAML format. And it’s really easy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
And the serialized_object looks like this:
1 2 3 4 5 6 7
And from the pry console, we can get what
1 2 3 4 5 6 7 8 9 10
Pretty easy, right?
The other serialization mechanism built into Ruby is binary serialization using Marshal. Its only difference with YAML is the not human readable format as it stores objects in a binary format.
1 2 3 4
The disadvantage of Marshal is obvious: output is not human-readable. And the advantage is its speed compared to YAML.
So, What else? Actually In Ruby community, the most commonly used serialization mechanism is JSON. The JSON support in Ruby is provided by serveral libraries, such as json, json_pure (Pure Ruby Implementation), Yajl, Oj.
Then, why JSON? We already have YAML format, right? We choose JSON not only because it’s human readable(more than YAML), but also it can be used to transport data by AJAX calls.
Now let’s see what differences are between these implementations.
json_pure gem, if you are serializing an object which is not a hash, arrray, or primitive, you have to write more codes to make sure the object is serializable. Let’s take
class A for an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
We can see the output like this:
And from the pry console:
1 2 3 4
As you can see, we should implement two methods to make our custom object serialization work:
- to_json – called on the object instance and allows us to convert an object into a JSON string.
- json_create – allows us to call
JSON.loadpassing in a JSON string which will convert the string into an instance of our object
YAJL is a C binding to the excellent YAJL JSON parsing and generation library. And according to author’s benchmarks, it’s faster than
- ~3.5x faster than JSON.generate
- ~1.9x faster than JSON.parse
- ~4.5x faster than YAML.load
- ~377.5x faster than YAML.dump
- ~1.5x faster than Marshal.load
- ~2x faster than Marshal.dump
yajl-ruby doesn’t support serializing custom objects, and you could see the benchmark results don’t include comparison with
Oj stands for Optimized JSON. Here is a simple example:
1 2 3 4 5 6 7
And remember our custom class
A? You don’t have to write two more methods like
Oj would take care for you, and you can see how it does that through the json_string it generates.
1 2 3 4 5 6
It uses a special string “^o” to mark your custom object’s class!
1 2 3 4
Here is the fun part, let’s write some benchmarks to compare these mechanisms’ performance.
Basic Ruby data types deserialization:
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8
And you could see,
oj has the best performance.
What about custom objects?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
1 2 3 4 5 6
Actually, the author of
oj gem did a more detailed performance comparisons before, also compared with native
Marshal, and still a win.
- Strict Mode Performance: To benchmark the Oj strict parser a Ruby Object was selected that included all the various types that can be expected in a JSON document as well as some nested arrays and hashes to add some variety.
- Compat Mode Performance: Yajl was not included as it does not support the feature of encoding Object that respond to the to_json() or to_hash() method and does not support the create_id feature.
- Object Mode Performance: There are no other JSON parsers that support Object encoding and decoding but the Ruby Marshal module does encode and decode Ruby Objects into a binary format (@larry: by
no other JSON parsers, I think the author was tring to even
jsonkind of suports it, but it needs more implementations.). Another efficient Object encoder is Ox which encodes Objects into XML. Oj is compared to these modules.
So apparently, if you use MRI, the
oj gem might be your best choice.
And this week, I will blog more about JSON serialization in Rails.
See you soon!