如何用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
相关产品推荐
相关产品推荐

