You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用active_model_serializers定制多对象JSON输出(含空值过滤)

问题

我正在学习Rails,使用active_model_serializers gem,需要从Car和Battery两个数据表生成定制化JSON输出。

表结构

  • car表:id, name, color, size, shape, date...
  • battery表:id, car_id, price, weight, name, date...

模型关联

class Battery < ApplicationRecord
  belongs_to :car
end

我已在控制器中计算得到三个哈希数据:available_cars、available_batteries、cars_with_battery,现在需要填充CarIndexListSerializer中的对应方法,生成指定格式的JSON,且当哈希为空时不显示对应字段。

期望输出格式

{
  "available_cars": [{"id": 1, "name": "name1", "size": "abc"}],
  "available_batteries": [{"id": 1, "name": "name1"}],
  "cars_with_battery": [{"id": 1, "name": "name1", "size": "abc", 
                         "batteries": [{"id": 1, "name": "name1"}]
                        }]
}

控制器代码

# cars_controller.rb
available_cars = { 
  available_cars: [{id: id, 
                    name: name, 
                    color: color, 
                    size: size, 
                    date: date...
                   }] 
}

available_batteries = {
  available_batteries: [{id: id, 
                         car_id: car_id, 
                         price: price, 
                         weight: weight, 
                         name: name, 
                         date: date...
                       }]
}

# 原代码存在语法错误,修正后如下
cars_with_batteries = {
  cars_with_battery: [{id: id, 
                      name: name, 
                      color: color, 
                      size: size, 
                      date: date...,
                      batteries: [{id: 1, 
                                 name: name1
                                }, 
                                {id: 2, 
                                 name: name2
                                }
                               ]
                      }]
}

render json: { 
        available_cars: available_cars, 
        available_batteries: available_batteries, 
        cars_with_battery: cars_with_batteries 
      },
      serializer: CarIndexListSerializer

序列化器代码

# car_index_list_serializer.rb
class CarIndexListSerializer < ActiveModel::Serializer
  attributes :available_cars,
             :available_batteries,
             :cars_with_battery

  def available_cars
      # todo?      
  end

  def available_batteries
      # todo?
  end

  def cars_with_battery
      # todo?
  end
end
解决方案

先修正控制器数据结构(可选但更简洁)

原控制器里每个变量都是套了一层哈希的数组,其实可以直接传递数组,简化后续序列化逻辑:

# 修正后的控制器变量定义
available_cars = [{id: id, name: name, color: color, size: size, date: date...}]
available_batteries = [{id: id, car_id: car_id, price: price, weight: weight, name: name, date: date...}]
cars_with_batteries = [{id: id, name: name, color: color, size: size, date: date..., batteries: [...] }]

# 渲染时直接传数组
render json: { 
        available_cars: available_cars, 
        available_batteries: available_batteries, 
        cars_with_battery: cars_with_batteries 
      },
      serializer: CarIndexListSerializer

实现序列化器方法

下面提供两种情况的实现,对应不同的控制器数据结构:

情况1:保留原控制器的哈希嵌套结构

class CarIndexListSerializer < ActiveModel::Serializer
  attributes :available_cars,
             :available_batteries,
             :cars_with_battery

  def available_cars
    # 取出内部的车辆数组
    cars = object[:available_cars][:available_cars]
    # 数组为空则返回nil,自动隐藏该字段
    return nil if cars.empty?
    
    # 筛选需要的字段,生成目标格式
    cars.map do |car|
      { id: car[:id], name: car[:name], size: car[:size] }
    end
  end

  def available_batteries
    batteries = object[:available_batteries][:available_batteries]
    return nil if batteries.empty?
    
    batteries.map do |battery|
      { id: battery[:id], name: battery[:name] }
    end
  end

  def cars_with_battery
    cars = object[:cars_with_battery][:cars_with_battery]
    return nil if cars.empty?
    
    cars.map do |car_data|
      # 组装车辆基础字段
      car_hash = { id: car_data[:id], name: car_data[:name], size: car_data[:size] }
      # 筛选电池需要的字段
      car_hash[:batteries] = car_data[:batteries].map do |battery|
        { id: battery[:id], name: battery[:name] }
      end
      car_hash
    end
  end
end

情况2:使用修正后的直接数组结构(更简洁)

class CarIndexListSerializer < ActiveModel::Serializer
  attributes :available_cars,
             :available_batteries,
             :cars_with_battery

  def available_cars
    cars = object[:available_cars]
    return nil if cars.empty?
    
    cars.map { |car| { id: car[:id], name: car[:name], size: car[:size] } }
  end

  def available_batteries
    batteries = object[:available_batteries]
    return nil if batteries.empty?
    
    batteries.map { |battery| { id: battery[:id], name: battery[:name] } }
  end

  def cars_with_battery
    cars = object[:cars_with_battery]
    return nil if cars.empty?
    
    cars.map do |car|
      {
        id: car[:id],
        name: car[:name],
        size: car[:size],
        batteries: car[:batteries].map { |b| { id: b[:id], name: b[:name] } }
      }
    end
  end
end

关键说明

  • 空字段自动隐藏:当序列化方法返回nil时,active_model_serializers会自动排除对应的属性,满足空值不显示的需求。
  • 字段筛选:通过map方法只保留期望输出中的字段,过滤掉不需要的color、date、price等冗余数据。
  • 语法修正:原控制器中cars_with_batteries的结构存在语法错误,必须把batteries数组嵌套在每个车辆的哈希内部,否则无法正确序列化。

内容的提问来源于stack exchange,提问作者SS Sid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 15:55:17