How to Exclude Inherited Fields in Rails Serializers

When working with ActiveModel::Serialization in a Ruby on Rails API, it’s common to organize serializers using inheritance—especially when building and maintaining different versions of your API.

One potential pitfall: Rails includes all attributes declared in the parent serializer by default, even when rendering from a child. This can lead to unexpected fields appearing in API responses for earlier versions.

Here’s how to address that.

The Context

Suppose a new serializer is created under API::V40, intended to include everything already exposed by API::V36::ProgramGuide. To avoid duplication, shared logic is moved to the V40 serializer, and the V36 version inherits from it.

The only difference is that V40 needs to expose an additional fieldabout_host — which must not appear in V36 responses.

The Problem

The V40 serializer might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# app/serializers/api/v40/accommodation_serializer.rb

module Api
  module V40
    class AccommodationSerializer < ActiveModel::Serializer
      attributes :main_description, :images_gallery, :about_host

      def main_description
        object.description
      end

      def images_gallery
        object.photos.map do |photo|
          {
            url: photo.file.url(:large),
            caption: photo.caption
          }
        end
      end

      def about_host
        [
          {
            title: I18n.t(".accommodations.what_offers"),
            description: object.what_offers_description
          }
        ]
      end
    end
  end
end

The V36 serializer inherits from V40 and adds two more attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# app/serializers/api/v36/program_guide/accommodation_serializer.rb

module Api
  module V36
    module ProgramGuide
      class AccommodationSerializer < Api::V40::AccommodationSerializer
        attributes :extra_information, :faq

        def extra_information
          object.accommodation_info_sections.sections.map do |info_section|
            ExtraInformationPreviewSerializer.new(info_section, root: false)
          end
        end

        def faq
          object.accommodation_info_sections.faq.map do |info_section|
            FaqPreviewSerializer.new(info_section, root: false)
          end
        end
      end
    end
  end
end

Even though only :extra_information and :faq are declared in the V36 serializer, Rails includes :main_description, :images_gallery, and :about_host in the JSON — since they’re inherited from the parent.

This creates a problem when a field like about_host is version-specific.

The Solution

To prevent specific attributes from being included in earlier versions, override the attributes method in the child serializer and explicitly exclude the undesired field:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# app/serializers/api/v36/program_guide/accommodation_serializer.rb

module Api
  module V36
    module ProgramGuide
      class AccommodationSerializer < Api::V40::AccommodationSerializer
        attributes :extra_information, :faq

        def extra_information
          object.accommodation_info_sections.sections.map do |info_section|
            ExtraInformationPreviewSerializer.new(info_section, root: false)
          end
        end

        def faq
          object.accommodation_info_sections.faq.map do |info_section|
            FaqPreviewSerializer.new(info_section, root: false)
          end
        end

        private

        def attributes(*args)
          super.except(:about_host)
        end
      end
    end
  end
end

Why This Works

The attributes(*args) method returns a hash of all key-value pairs that will be rendered in the response. By chaining .except(:about_host), only that specific field is removed, while all other inherited logic remains intact.

When to Use This

Use this approach when:

When Not to Use It

If serializers for different versions start to diverge significantly, inheritance may add more complexity than it removes. In such cases, defining serializers independently might offer better clarity and maintainability.