Active_model_serializers: render json: hash is processed by AMS, logs 'Rendered ActiveModel::Serializer::Null with Hash'

Created on 10 Jan 2017  Â·  17Comments  Â·  Source: rails-api/active_model_serializers

Expected behavior vs actual behavior

In my controller I still want to render an arbitrary Hash as json with an option to set response status. So, while in one action I'd like to use AMS implicitly with render json: @object , I'd still like to use something like render json: { message: message }, status: status without hitting the AMS.

Steps to reproduce

(e.g., detailed walkthrough, runnable script, example application)

def some_action
  ...
  message = 'Incorrect URL'
  status = :bad_request
  render json: { message: message }, status: status
end

Environment

ActiveModelSerializers Version (commit ref if not on tag): 0.10.4

Output of ruby -e "puts RUBY_DESCRIPTION": ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin15]

OS Type & Version: Mac OS X 10.11.6

Integrated application and version (e.g., Rails, Grape, etc): Rails 5.0.1
Default adapter.

Backtrace

(e.g., provide any applicable backtraces from your application)

[active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash (0.14ms)

I'd need to run some benchmark to determine if it actually slows down the response significantly or not.

Bug Ready for PR 0.10.x

All 17 comments

@januszm yeah, this is an annoying issue we need to fix. In the meantime, you should be able to render json: { message: message }.to_json, status: :bad_request

I don't think it actually is doing any transformation, just checking if it can find a serializer, and then returning the value when it can't, but it goes through the logger when doing that leaving that annoying message

@bf4 one other issue though is that when specifying adapter: false # or nil, SerializableResource still tries to fetch a serializer whereas it should not.

@bf4 thanks actually to_json was not needed after that hash. It's working fine as render json: { message: message }, status: status

Anyway, looks like the Null serializer is used >only< here, in the logger:
lib/active_model_serializers/logging.rb:85: serializer: serializer || ActiveModel::Serializer::Null

I've just tried a short circuit in LogSubscriber#render and removing the Null serializer , helped but I'm not sure where else you use that Null serializer.

    def notify_render_payload
      {
        serializer: serializer, # don't use Null serializer
        adapter: adapter || ActiveModelSerializers::Adapter::Null
        ...
    class LogSubscriber < ActiveSupport::LogSubscriber
      def render(event)
        return unless event.payload[:serializer] # render nothing 
        ...

We could mention that in the docs (eg. 'When you serialize a "hash" you'll see Null serializer mentioned in the logs').

Also, it feels like it shouldn't hit the SerializableResource unless the serialized resource is a model or association.

@januszm this should probably not appear in AMS logs or should appear as something like rendered without AMS: no serializer found. That is way clearer than specifying rended with Null serializer and mention it in the docs. There are some fixes to be done here.

@groyoh yes I agree, that's why I tried the patch above which removes the message from the logs entirely (notice return unless event.payload[:serializer]). I also agree that there are some fixes to be done, that's what I mentioned that in my last phrase, I wouldn't touch the SerializableResource class unless the object that we want to serialize is somehow related to ActiveModel / ActiveRecord.

this should probably not appear in AMS logs
@groyoh yes, it's an undesired effect of a change (bug fix) in how serializable resource works in the controller...
as https://github.com/rails-api/active_model_serializers/issues/2024#issuecomment-271719008

specifying adapter: false # or nil, SerializableResource still tries to fetch a serializer


        warn 'ActionController::Serialization#use_adapter? has been removed. '\
          "Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
        options[:adapter] = false

yeah, I guess get_serializer could return early for that... code design...

https://github.com/rails-api/active_model_serializers/blob/93ca27fe444c3a940890cb7a4dedbd8369f481f3/lib/action_controller/serialization.rb#L45-L48

      # For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
      # Otherwise, since `serializable_resource` is not a string, the renderer would call
      # `to_json` on a String and given odd results, such as `"".to_json #=> '""'`
      serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource

might want to check if adapter is an adapter, instead...

Does anyone know has a fix been identified for this issue yet? Or a work around? Looks like it's ready for PR for a couple of months now 🤔

@travega Not fixed. Not one's done it :)

I have these notes in my initializer that might be helpful. If you're using an exceptions_app that inherits from action controller, you may need to set serialization_scope :view_context # so ActiveModelSerializers doesn't call current_user

ActiveSupport.on_load(:action_controller) do
  require 'active_model_serializers/register_jsonapi_renderer'
  ActiveModelSerializers.eager_load! if Rails.env.production?
  ActiveModelSerializers::Adapter::JsonApi.eager_load! if Rails.env.production?
  require 'active_model/serializer/null'
  ActiveModel::Serializer::Null
  # https://github.com/rails-api/active_model_serializers/blob/a032201a91cbca407211bca0392ba881eef1f7ba/lib/active_model_serializers/logging.rb#L83-L87
  #   def notify_render_payload
  #     {
  #       serializer: serializer || ActiveModel::Serializer::Null,
  #       adapter: adapter || ActiveModelSerializers::Adapter::Null
  #     }
  #   end
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:85:in `notify_render_payload'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:78:in `notify_render'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:21:in `block (2 levels) in instrument_rendering'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:97:in `tag_logger'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:20:in `block in instrument_rendering'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.7.1/lib/active_support/callbacks.rb:81:in `run_callbacks'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/active_model_serializers/logging.rb:68:in `block (2 levels) in notify'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/renderers.rb:116:in `block in <module:Renderers>'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/active_model_serializers-0.10.2/lib/action_controller/serialization.rb:53:in `block (2 levels) in <module:Serialization>'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/renderers.rb:45:in `block in _render_to_body_with_renderer'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/renderers.rb:41:in `_render_to_body_with_renderer'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/renderers.rb:37:in `render_to_body'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/instrumentation.rb:43:in `render'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/remotipart-1.2.1/lib/remotipart/render_overrides.rb:14:in `render_with_remotipart'
  #   /srv/rails_server/app/controllers/exception_handler/exception_controller.rb:21:in `block (2 levels) in show'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/mime_responds.rb:217:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.7.1/lib/action_controller/metal/mime_responds.rb:217:in `respond_to'
  #   /srv/rails_server/app/controllers/exception_handler/exception_controller.rb:20:in `show'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/lograge-0.4.1/lib/lograge/rails_ext/rack/logger.rb:15:in `call_app'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:22:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/sendfile.rb:113:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/skylight-1.0.0/lib/skylight/middleware.rb:61:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/railties-4.2.7.1/lib/rails/engine.rb:518:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/railties-4.2.7.1/lib/rails/application.rb:165:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/configuration.rb:225:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/server.rb:569:in `handle_request'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/server.rb:406:in `process_client'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/server.rb:271:in `block in run'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/thread_pool.rb:116:in `call'
  #   /srv/rails_server/vendor/bundle/ruby/2.2.0/gems/puma-3.5.2/lib/puma/thread_pool.rb:116:in `block in spawn_thread't
end

I have the same problem. When I use AMS implicitly with render json: @data, it's work perfect. But when I use it in hash:

def json_response messages, data, status = :ok
    render json: {
      messages: messages,
      data: data
    }, status: status
end

I got error: Rendered ActiveModel::Serializer::Null with Hash.
Does anyone solve it ?

Call to_json on your hash

B mobile phone

On Aug 9, 2017, at 8:44 PM, Tran B. V. Son notifications@github.com wrote:

I have the same problem. When I use AMS implicitly with render json: @data, it's work perfect. But when I use it in hash:

def json_response messages, data, status = :ok
render json: {
messages: messages,
data: data
}, status: status
end
I got error: Rendered ActiveModel::Serializer::Null with Hash.
Does anyone solve it ?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@bf4 It doesn't work :(

def json_response messages, data, status = :ok
    render json: {
      messages: messages,
      data: data
    }.to_json, status: status
end

Well, without any more details on how it doesn't work I don't have anything tp add except don't serialize hashes... uses either records or strings

B mobile phone

On Aug 9, 2017, at 9:20 PM, Tran B. V. Son notifications@github.com wrote:

@bf4 It doesn't work :(

def json_response messages, data, status = :ok
render json: {
messages: messages,
data: data
}.to_json, status: status
end
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

Actually to_json worked for me while sending hash in render render json: @status.to_json and i didn't get any message like Rendered ActiveModel::Serializer::Null with Hash after rendering but it should not be use to_json like that

@sree300994

after rendering but it should not be use to_json like that

Strongly disagree. AMS by design ignores any string, such as the result of to_json. So, this is a natural way to skip AMS. AMS doesn't serialize a Hash because it's a primitive, not a model. Discussed in readme.

yaa its correct maybe their might be change in naming convention of serializer. what i mean is if the controller in normal controllers folder and the serializer might be created in versioning means it will through this error. The example is class UsersController < ApplicationController this is how i created controller but the serializer i created is like class Api::V1::UserSerializer < ActiveModel::Serializer by creating like this it thrown me error, after changing serializer to class UserSerializer < ActiveModel::Serializer this, my error disappeared

yaa its correct maybe their might be change in naming convention of serializer. what i mean is if the controller in normal controllers folder and the serializer might be created in versioning means it will through this error. The example is class UsersController < ApplicationController this is how i created controller but the serializer i created is like class Api::V1::UserSerializer < ActiveModel::Serializer by creating like this it thrown me error, after changing serializer to class UserSerializer < ActiveModel::Serializer this, my error disappeared

When serializing a model inside a namespace, such as Api::V1::Post, ActiveModelSerializers will expect the corresponding serializer to be inside the same namespace (namely Api::V1::PostSerializer).

Was this page helpful?
0 / 5 - 0 ratings