oj: Inconsistency with JSON.generate in context of a Rails app
In a freshly generated Rails app (created by just running rails new my_app), with only the addition of the oj gem to the Gemfile, assuming that we have a class like:
class Foo
def initialize
@x = 123
end
end
Then, Oj.mimic_JSON() (or, equivalently, Oj.optimize_rails()) fails to mimic the serialization behavior. In a rails console:
foo = Foo.new
JSON.generate(foo)
# => "\"#<Foo:0x00007fc9e0f294f8>\""
Oj.mimic_JSON()
JSON.generate(foo)
# => "{\"x\":123}"
As seen here, Oj serializes the object by serializing its instance variables (probably due to calling foo.as_json internally), differently from JSON which just prints the object as a string.
Note that in Rails using the default JSON, the behavior of object.to_json and JSON.generate(object) differs in that only object.to_json relies on calling object.as_json, but with Oj.mimic_JSON this difference is lost:
class X
def as_json(arg)
{ abc: 123 }
end
end
x = X.new
# In a Rails app, before requiring Oj:
x.to_json
# => "{\"abc\":123}"
JSON.generate(x)
# => "\"#<X:0x00007fc8a52ca4c8>\""
# When requiring Oj and mimicking JSON:
require 'oj'
Oj.mimic_JSON()
x.to_json
# => "{\"abc\":123}"
JSON.generate(x)
# => "{\"abc\":123}"
In other words, to correctly mimic the behavior of the JSON gem in a Rails app, Oj should call object.as_json when calling object.to_json, but should NOT call object.as_json when calling JSON.generate(object).
I am running this code in Ruby 2.6.6 and Rails 6.1.3.1, not sure if results vary by version. In a plain Ruby project (not requiring Rails) this problem does not seem to occur, and JSON.generate behaves like the JSON gem version when serializing instances of custom classes. Conversely, if require 'rails' is added before calling Oj.mimic_JSON(), the issue occurs.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (8 by maintainers)
I can verify that without Rails all seems fine. It also appears as if in requiring rails does not change the JSON.generate so that is indeed an issue. I’ll continue working on a fix.
I’ll dig into the issue this weekend.
Released
It looks like sometimes a Hash is passed as well according to the JSON gem unit tests. In any case adding a State if neither a Hash or State is already included seems to work. Feel free to try the current generate-special branch.
I’ll explore that avenue a bit tonight. Sadly it really is not clear cut.
I’m not sure if it is rails or the json gem but what appears to be happening is that
JSON.generateafter pulling in rails will ignore theto_jsonmonkey patched by ActiveSupport but will honour all others. I’m sure there is some code somewhere that defines something more clear but I have not been able to find it. I think the best I can do is inJSON.generatejust ignore anyto_jsondefined by ActiveSupport. Special case always slow the code down but that’s the penalty for trying to be compatible I suppose.