Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert symbols to string when serializing with Oj.dump

Problem Summary: I am attempting to serialize a hash to JSON using the Oj gem. It seems that Oj does not automatically convert the symbol keys of the hash to strings. I am wondering if Oj has an option to "stringify" during serialization?

This is an example of my hash:

example_hash = 
 {:id=>1234,
  :asset_number=>"1234-5678",
  :latitude=>34.78495,
  :longitude=>-92.12899,
  :last_tracking_record_id=>123456789,
  :bearing=>42,
  :threat_level=>:severe}

And the above is rendered like so:

render json: Oj.dump(example_hash)

The resulting JSON unfortunately has keys that look exactly like the above, meaning I would need to access elements in JavaScript like so: response[:asset_number]. Since the client-side code was implemented months ago, and have only now added Oj, I would prefer to find a way to stringify the keys during serialization server-side.

Oj has an option called symbol_keys, which is a boolean, however setting it to either true or false seems to have no effect in this regard.

The only solution I have found thus far is to use with_indifferent_access as suggested in this answer, however in some cases I have arrays of hashes; while I can technically invoke that method for each hash in that array, given that Oj is meant to speed up Json serialization I would prefer to find a way to do this with Oj itself. Ultimately, I wonder if there is an option or setting in Oj that will perform this during serialization.

like image 936
Paul Richter Avatar asked Jan 09 '14 19:01

Paul Richter


2 Answers

While I was writing the question, I was able to find the answer. Since I cannot find any other answers on StackOverflow relating to this issue (specifically with regards to the Oj gem), I will keep this post in the hopes it will help others in my situation.

According to this previously discussed issue on GitHub, setting the option mode to :compat will indeed convert the symbols to strings. So my render line now looks like this:

render json: Oj.dump(example_hash, mode: :compat)

According to the Oj documentation for default_options, :compat mode is defined as follows:

...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.

So if I am interpreting that correctly, it seems this solution works because it is ultimately using the to_json method of the Hash class.

I am uncertain whether I have affected performance (either positively or negatively), but at least it saves me from having to manually invoke with_indifferent_access or to_json in the case of an array.

Update

In regards to performance, cmwright did some benchmarking, and came up with these results:

Rehearsal ----------------------------------------------
json        13.990000   0.250000  14.240000 ( 14.257051)
oj default   3.260000   0.230000   3.490000 (  3.491028)
oj compat    3.360000   0.240000   3.600000 (  3.593109)
------------------------------------ total: 21.330000sec

                 user     system      total        real
json        13.740000   0.240000  13.980000 ( 13.992641)
oj default   3.020000   0.230000   3.250000 (  3.248077)
oj compat    3.060000   0.230000   3.290000 (  3.286443)

Seems like the compat option is at least on par with the default Oj options, and significantly more efficient than plain 'ol to_json.

This is the gist containing the benchmark code.

like image 74
Paul Richter Avatar answered Oct 20 '22 10:10

Paul Richter


Using the generate method gives you the same output.

Oj.generate({a: `test`}) 
like image 21
DenicioCode Avatar answered Oct 20 '22 10:10

DenicioCode