Reputation: 423
I'm having problems with weird behaviour in RoR. I'm having a Hash that i'm converting to json using to_json()
like so:
data = Hash.new
# ...
data = data.to_json()
This code appears inside a model class. Basically, I'm converting the hash to JSON when saving to database. The problem is, the string gets saved to database with its surrounding quotes. For example, saving an empty hash results in: "{}"
. This quoted string fails to parse when loading from the database.
How do I get rid of the quotes?
The code is:
def do_before_save
@_data = self.data
self.data = self.data.to_json()
end
EDIT:
Due to confusions, I'm showing my entire model class
require 'json'
class User::User < ActiveRecord::Base
after_find { |user|
user.data = JSON.parse(user.data)
}
after_initialize { |user|
self.data = Hash.new unless self.data
}
before_save :do_before_save
after_save :do_after_save
private
def do_before_save
@_data = self.data
self.data = self.data.to_json()
end
def do_after_save
self.data = @_data
end
end
The data
field is TEXT
in mysql.
Upvotes: 3
Views: 1611
Reputation: 66
use {}.as_json instead of {}.to_json
ex:
a = {}
a.as_json # => {}
a.to_json # => "{}"
http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
Upvotes: 0
Reputation: 102249
If you are saving hash as a JSON string in a varchar column you can use serialize
to handle marshalling/unmarshaling the data:
class Thing < ActiveRecord::Base
serialize :foo, JSON
end
Knowing exactly when to convert the data in the lifecycle of a record is actually quite a bit harder than your naive implementation. So don't reinvent the wheel.
However a huge drawback is that the data cannot be queried in the DB*. If you are using Postgres or MySQL you can instead use a JSON or JSONB (postgres only) column type which allows querying. This example is from the Rails guide docs:
# db/migrate/20131220144913_create_events.rb
create_table :events do |t|
t.json 'payload'
end
# app/models/event.rb
class Event < ApplicationRecord
end
# Usage
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
event = Event.first
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}
## Query based on JSON document
# The -> operator returns the original JSON type (which might be an object), whereas ->> returns text
Event.where("payload->>'kind' = ?", "user_renamed")
Upvotes: 0
Reputation: 230521
I'm willing to bet money that this is the result of you calling .to_json
on the same data twice (without parsing it in between). I've had a fair share of these problems before I devised a rule: "don't mutate data in a lossy way like this".
If your original data was {}
, then first .to_json
would produce "{}"
. But if you were to jsonify it again, you'd get "\"{}\""
because a string is a valid json data type.
I suggest that you put a breakpoint in your before_save filter and see who's calling it the second time and why.
"call .to_json
twice" is a general description and can also mean that there are two subsequent saves on the same object, and since self.data
is reassigned, this leads to data corruption. (thanks, @mudasobwa)
Upvotes: 4
Reputation: 2745
It depends on your model's database field type.
If the field is string type (like VARCHAR or TEXT) it should be stored as string (no need to get rid of the quotes - they are fine). Make sure calling to_json
once.
If the field is Postgres JSON type, then you can just assign a hash to the model's field, no need to call to_json
at all.
Upvotes: 0