2

Using Active Model Serializer, is there an easy and integrated way to return a JSON "object" (that would then be converted in a javascript object by the client framework) instead of a JSON "array" when serializing a collection? (I am quoting object and array, since the returned JSON is by essence a string).

Let's say I have the following ArticleSerializer:

class ArticleSerializer < ActiveModel::Serializer
  attributes :id, :body, :posted_at, :status, :teaser, :title
end

I call it from ArticlesController:

class ArticlesController < ApplicationController
  def index
    @feed = Feed.new(articles: Article.all)
    render json: @feed.articles, each_serializer: ArticleSerializer
  end
end

Is there a way to pass an option to the serializer to make it return something like:

{"articles":
  {
    "1":{ 
      ...
    },
    "2":{
      ...
    }
  }
}

instead of

{"articles":
  [
    { 
      "id":"1",
      ...
    },
    {
      "id":"2"
      ...
    }
  ]
}

Edit: I guess that the approach proposed in this post (subclassing AMS ArraySerializer) might be helpful (Active Model Serializer and Custom JSON Structure)

2 Answers 2

3

You'd have to write a custom adapter to suit your format. Alternatively, you could modify the hash before passing it to render. If you do not mind iterating over the resulting hash, you could do:

ams_hash = ActiveModel::SerializableResource.new(@articles)
                                            .serializable_hash
result_hash = ams_hash['articles'].map { |article| { article['id'] => article.except(:id) } }
                                  .reduce({}, :merge)

Or, if you'd like this to be the default behavior, I'd suggest switching to the Attributes adapter (which is exactly the same as the Json adapter, except there is no document root), and override the serializable_hash method as follows:

 def format_resource(res)
   { res['id'] => res.except(:id) }
 end

 def serializable_hash(*args)
   hash = super(*args)
   if hash.is_a?(Array)
     hash.map(&:format_resource).reduce({}, :merge)
   else
     format_resource(hash)
   end
 end
Sign up to request clarification or add additional context in comments.

3 Comments

You're right. Looking at the source code, no such option seem available. I guess I have to amend method serializable_hash_for_collection in the json_api adapter link. Or modify the hash, using a RegEx for the best performance. Or iterate over my collection of Articles and call the single item serializer on each of them and wrap them up in a hash before passing it to render. Thanks a lot!!!
@Cedric Have a look at the solution I just sketched!
this looks really good. I'll give it a try tomorrow and will let you know if works, but it looks really good. Merci beaucoup!
0

No, semantically you are returning an array of Articles. Hashes are simply objects in Javascript, so you essentially want an object with a 1..n method that returns each Article, but that would not make much sense.

1 Comment

Thanks. It might not make sense but it is exactly what I am forced to do in my js framework with normalizrjs... And this pattern of storing array-like structures as flat objects is the way flat database store their data. I could write a hand-made serializer that would iterate over each of my articles, fetch their ID, pass them individually to the AMS serializer, and build a JSON flat object/hash out of them before passing them back to the controller, and then render it as JSON. But performance-wise... The purpose is to alleviate the front-end. I might try a REGEX instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.