Search code examples
ruby-on-railsrubyserializationactive-model-serializers

Serializing Nested Attributes Active Model Serializer


I have the following code and can't seem to get my JSON to output as per my serializer.

I receive the following log [active_model_serializers] Rendered SimpleJobSerializer with Hash

My controller is as below:

# Jobs Controller

def home
    return_limit = 2

    @dev_jobs = Job.where(category: 'developer').limit(return_limit)
    @marketing_jobs = Job.where(category: 'marketing').limit(return_limit)
    @sales_jobs = Job.where(category: 'sales').limit(return_limit)


    @jobs = {
      developer: @dev_jobs,
      marketing: @marketing_jobs,
      sales: @sales_jobs
    }

    render json: @jobs, each_serializer: SimpleJobSerializer
  end

And my serializer:

class SimpleJobSerializer < ActiveModel::Serializer
  attributes :title, :company, :job_type, :date

  def date
    d = object.created_at
    d.strftime("%d %b")
  end
end

I am receiving the full API response but expect to only receive title, company, job_type and date.

It's worth mentioning the jobs model is completely flat and there are currently no associations to take into account. It seems to be just the nesting of the jobs into the @jobs object that's stopping serialization.

Any help would be much appreciated.


Solution

  • each_serializer expects you to pass an array but here you're passing a hash:

    @jobs = {
      developer: @dev_jobs,
      marketing: @marketing_jobs,
      sales: @sales_jobs
    }
    

    Since you want that structure, I'd recommend two approachs depending on which you prefer. One is to change the serializer which should control the format:

    class JobsSerializer < ActiveModel::Serializer
      attributes :developer, :marketing, :sales
    
      def developer
        json_array(object.where(category: "developer"))
      end
    
      def marketing
        json_array(object.where(category: "marketing"))
      end
    
      def sales
        json_array(object.where(category: "sales"))
      end
    
      def json_array(jobs)
        ActiveModel::ArraySerializer.new(jobs, each_serializer: SimpleJobSerializer)
      end
    end
    

    You can still leave your current serializer as is:

    class SimpleJobSerializer < ActiveModel::Serializer
      attributes :title, :company, :job_type, :date
    
      def date
        d = object.created_at
        d.strftime("%d %b")
      end
    end
    

    Or option 2 would be to do this in the controller:

    @jobs = {
      developer: ActiveModel::ArraySerializer.new(@dev_Jobs, each_serializer: SimpleJobSerializer),
      marketing: ActiveModel::ArraySerializer.new(@marketing_jobs, each_serializer: SimpleJobSerializer),
      sales: ActiveModel::ArraySerializer.new(@sales_jobs, each_serializer: SimpleJobSerializer)
    }
    
    render json: @jobs