JSON for ActiveRecord (deserialization)
I am creating web API with Ruby, Grape and ActiveRecord. Coming from ASP.NET Web API, I'm used to auto-binding a model from JSON to a class object, which can then be persisted using the Entity Framework. I've searched a bit to see if there is something like this when using ActiveRecord, but haven't found anything that makes me think I'm missing something really obvious. What's the best way to deserialize JSON in an ActiveRecord model?
UPDATE
Matts answer works great for simple types, but when I have associations I get the following error: ActiveRecord :: AssociationTypeMismatch: Connector (# 70297711668780) expected, got Hash (# 70297701477140)
This is the code I am running
class Card < ActiveRecord::Base
has_many :connectors, autosave: true
end
class Connector < ActiveRecord::Base
has_one :card
has_one :connected_card, :class_name => "Card", :foreign_key => "connected_card_id"
end
json = "{\"id\":\"1\", \"connectors\": [{\"id\": 1, \"card_id\":1}]}"
card = Card.new(ActiveSupport::JSON.decode(json))
source to share
In your model, Card
you have created an association with connector names. This is a method ActiveRecord::Relation
and it will banish a bunch of models from the class Connector
. Otherwise, you are passing in an array of hashes.
{"id"=>"1", "connectors"=>[{"id"=>1, "card_id"=>1}]}
In your method, #new
it will look something like this:
Card.new(id: 1, connectors: [{id: 1, card_id: 1}])
Once you get there, the array of connectors should be populated with connector instances, not hashes. And why it fails.
You have two options. The first one is renaming the key to before_action in the controller and using nested attributes:
class Card < ActiveRecord::Base
has_many :connectors
accepts_nested_attributes_for :connectors
end
class CardsController < ApplicationController
before_action :set_nested
private
def set_nested
params["connectors_attributes"] = params.delete("connectors")
end
end
Another option is to create a callback in the model to create objects from the hash.
class Card < ActiveRecord::Base
def initialize(params={})
params["connectors"] = params["connectors"].map {|connector| Connector.new(connector)}
super(params)
end
end
Any of the above options will suit your needs. IMO the best one is the first one as you won't be overridingActiveRecord#Methods
source to share
You can use standard JSON decoding from ActiveSupport
which will give you a hash.
Then you need to create a new object ActiveRecord
from this hash.
my_object = Foo.new(ActiveSupport::JSON.decode(some_json))
Translate new
for create
if you want to keep it on one line.
source to share