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))

      

+3


source to share


2 answers


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

+1


source


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.

+5


source







All Articles