Gotcha using Oj to generate JSON
source link: https://ylan.segal-family.com/blog/2021/12/12/gotcha-using-oj-to-generate-json/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Gotcha using Oj to generate JSON
Dec 12, 2021 • Ylan Segal • ruby, rails
Oj is a Ruby gem that bills itself as a faster way to generate JSON, mainly through the use of a C extension. I recently found it was generating unexpected results.
I was looking through a report that one of our endpoints was generating unusually large JSON payloads. In particular, timestamps where being serialized to a very verbose (and not very useful format):
{
"created_at": {
"^o": "ActiveSupport::TimeWithZone",
"utc": {
"^t": 1639339673.031328000
},
"time": null,
"time_zone": {
"^o": "ActiveSupport::TimeZone",
"name": "UTC",
"utc_offset": null,
"tzinfo": {
"^o": "TZInfo::DataTimezone",
"info": {
"^o": "TZInfo::ZoneinfoTimezoneInfo",
"identifier": "Etc/UTC",
"offsets": {
"^#1": [0, {
"^o": "TZInfo::TimezoneOffset",
"utc_offset": 0,
"std_offset": 0,
"abbreviation": ":UTC",
"utc_total_offset": 0
}]
},
"transitions": [],
"previous_offset": {
"^o": "TZInfo::TimezoneOffset",
"utc_offset": 0,
"std_offset": 0,
"abbreviation": ":UTC",
"utc_total_offset": 0
},
"transitions_index": null
}
}
},
"period": null
}
}
I quickly saw that the controller was invoking Oj
directly, and that is the root of the problem. The library has a Rails compatibility mode, that is not the default:
ts = Time.zone.now
ts.to_json
# => "\"2021-12-12T20:10:56Z\""
Oj.dump(ts)
# => "{\"^o\":\"ActiveSupport::TimeWithZone\",\"utc\":{\"^t\":1639339856.001998000},\"time\":{\"^t\":1639339856.001998000},\"time_zone\":{\"^o\":\"ActiveSupport::TimeZone\",\"name\":\"UTC\",\"utc_offset\":null,\"tzinfo\":{\"^o\":\"TZInfo::DataTimezone\",\"info\":{\"^o\":\"TZInfo::ZoneinfoTimezoneInfo\",\"identifier\":\"Etc/UTC\",\"offsets\":{\"^#1\":[0,{\"^o\":\"TZInfo::TimezoneOffset\",\"utc_offset\":0,\"std_offset\":0,\"abbreviation\":\":UTC\",\"utc_total_offset\":0}]},\"transitions\":[],\"previous_offset\":{\"^o\":\"TZInfo::TimezoneOffset\",\"utc_offset\":0,\"std_offset\":0,\"abbreviation\":\":UTC\",\"utc_total_offset\":0},\"transitions_index\":null}}},\"period\":{\"^o\":\"TZInfo::TimezonePeriod\",\"start_transition\":null,\"end_transition\":null,\"offset\":{\"^o\":\"TZInfo::TimezoneOffset\",\"utc_offset\":0,\"std_offset\":0,\"abbreviation\":\":UTC\",\"utc_total_offset\":0},\"utc_total_offset_rational\":null}}"
Oj.dump(ts, mode: :rails)
# => "\"2021-12-12T20:10:56Z\""
Adding mode: :rails
to the Oj
call fixed the unexpected payload size issue.
The fact that we had a production endpoint generating unexpected JSON for months lets me know two things:
- There is no test coverage that checks the generated JSON against a known schema
- Consumers of this internal endpoint have no use for the timestamps that were being sent down: There is no code that recognizes that data structure.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK