I'm building a web API with Ruby, Grape and ActiveRecord. Coming from ASP.NET Web API I'm used to having automatic model binding from JSON to a class object which then can be saved with Entity Framework. I have been searching a bit to find out if there is anything similiar to this when using ActiveRecord but haven't found anything which make me think that I'm missing something really obvious. What is the best way to deserialize JSON into ActiveRecord models?
UPDATE
Matts answer worked great for simple types but when I'm having associations I get the following error: ActiveRecord::AssociationTypeMismatch: Connector(#70297711668780) expected, got Hash(#70297701477140)
This is the code I'm 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))
You can use standard JSON decoding from ActiveSupport
, which will give you a hash.
You will need to then build a new ActiveRecord
object from that hash.
my_object = Foo.new(ActiveSupport::JSON.decode(some_json))
Swap new
for create
if you want it to save in one line.
In your model Card
you have created an association named connectors. This is an ActiveRecord::Relation
method, and it will be expecting a bunch of models from the class Connector
. You, otherwise, are passing an array of hashes.
{"id"=>"1", "connectors"=>[{"id"=>1, "card_id"=>1}]}
In to your #new
method this becomes something like:
Card.new(id: 1, connectors: [{id: 1, card_id: 1}])
Once you get there, the connectors array should be filled with Connector instances, not hashes. And that's why is failing.
You have two options. First is to rename the key in a before_action in the controller and use 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 the 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 will fit to your needs. IMO the best one is the first, as you won't be redefining ActiveRecord#Methods
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With