Active Model Serializers: Adding extra information outside root in ArraySerializer
Asked Answered
P

4

13

Say I have a model User and a serializer UserSerializer < ActiveModel::Serializer, and a controller that looks like this:

class UsersController < ApplicationController
  respond_to :json
  def index
    respond_with User.all
  end
end

Now if I visit /users I'll get a JSON response that looks like this:

{
  "users": [
    {
      "id": 7,
      "name": "George"
    },
    {
      "id": 8,
      "name": "Dave"
    }
    .
    .
    .
  ]
}

But what if I want to include some extra information in the JSON response that isn't relevant to any one particular User? E.g.:

{
  "time": "2014-01-06 16:52 GMT",
  "url": "http://www.example.com", 
  "noOfUsers": 2,
  "users": [
    {
      "id": 7,
      "name": "George"
    },
    {
      "id": 8,
      "name": "Dave"
    }
    .
    .
    .
  ]
}

This example is contrived but it's a good approximation of what I want to achieve. Is this possible with active model serializers? (Perhaps by subclassing ActiveModel::ArraySerializer? I couldn't figure it out). How do I add extra root elements?

Pilaf answered 6/1, 2014 at 9:54 Comment(1)
Try render :json => User.all Instead of respond_with, also look at as_json it might helpful.Thirzi
P
4

Got it working using render:

render json: {
  "time": "2014-01-06 16:52 GMT",
  "url": "http://www.example.com", 
  "noOfUsers": 2,
  "users": @users
}

The problem is, this doesn't call UserSerializer, it just calls .as_json on each individual user object and skips the Serializer. So I had to do it explicitly:

def index
  .
  .
  .
  render json: {
    "time": "2014-01-06 16:52 GMT",
    "url": "http://www.example.com", 
    "noOfUsers": 2,
    "users": serialized_users
  }
end

def serialized_users
  ActiveModel::ArraySerializer.new(@users).as_json
end

Not the most elegant solution but it works.

Pilaf answered 7/1, 2014 at 5:46 Comment(0)
A
11

You can pass them as the second arguement to respond_with

def index
 respond_with User.all, meta: {time: "2014-01-06 16:52 GMT",url: "http://www.example.com", noOfUsers: 2}
end

In version 0.9.3 in an initializer set ActiveModel::Serializer.root = true:

ActiveSupport.on_load(:active_model_serializers) do
  # Disable for all serializers (except ArraySerializer)
  ActiveModel::Serializer.root = true
end

In controller

render json: @user,  meta: { total: 10 }
Alnico answered 6/1, 2014 at 10:3 Comment(2)
This works for me on ActiveModelSerializers 0.8.1. See github.com/rails-api/active_model_serializers/blob/0-8-stable/…. Looks like this has changed in master, see github.com/rails-api/active_model_serializers/blob/…Mccurry
Also note this is for arrays only, if you want metadata in an instance, set call #meta= on the Serializer instance itself.Mccurry
P
4

Got it working using render:

render json: {
  "time": "2014-01-06 16:52 GMT",
  "url": "http://www.example.com", 
  "noOfUsers": 2,
  "users": @users
}

The problem is, this doesn't call UserSerializer, it just calls .as_json on each individual user object and skips the Serializer. So I had to do it explicitly:

def index
  .
  .
  .
  render json: {
    "time": "2014-01-06 16:52 GMT",
    "url": "http://www.example.com", 
    "noOfUsers": 2,
    "users": serialized_users
  }
end

def serialized_users
  ActiveModel::ArraySerializer.new(@users).as_json
end

Not the most elegant solution but it works.

Pilaf answered 7/1, 2014 at 5:46 Comment(0)
C
0

Just a simple hack if you don't want to modify either the serializer or render:

data = serializer.new(object, root: false)
# cannot modify data here since it is a serializer class
data = data.as_json
# do whatever to data as a Hash and pass the result for render
data[:extra] = 'extra stuff'
render json: data
Coniferous answered 10/8, 2017 at 7:50 Comment(0)
N
0

I was able to get this working for my use case by just adding the following in my controller. Nothing else needed with AMS 0.10.

render 
    json: @user,  
    meta: {
        time: "2014-01-06 16:52 GMT", 
        url: "http://www.example.com", 
        noOfUsers: 2
    }
Numbers answered 28/1, 2018 at 18:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.