如何在Rails控制器外递归使用ActiveModel::Serializers?
我之前也碰到过这个问题!控制器里的render json: @record, include: ['**']能完美递归所有层级,但直接用序列化器实例的as_json就卡在上一层,确实挺烦人的。问题根源在于:单独调用序列化器时,include选项不会自动传递给嵌套的子序列化器,而控制器的render方法内部会帮我们处理这个递归传递的逻辑。
下面给你两个可行的解决办法:
方法一:使用serializable_hash并显式传递include参数
ActiveModel::Serializers的serializable_hash方法其实支持传递include和其他序列化选项,而且它会正确把这些选项传递给嵌套的序列化器。你可以这样写:
# 替换成你的序列化器类和目标记录 serializer = RecordSerializer.new(@record) serialized_data = serializer.serializable_hash(include: ['**']) # 如果需要转成JSON字符串,再调用to_json json_string = serialized_data.to_json
这样就能得到和控制器里完全一致的多层嵌套结果——比如你的例子里,grandchild的数据也会被正确序列化出来。
方法二:给序列化器设置默认的include规则
如果这个序列化器经常需要递归嵌套,你可以直接在序列化器类里配置默认的include,这样每次实例化的时候就不用手动传参数了:
class RecordSerializer < ActiveModel::Serializer attributes :name belongs_to :child # 设置默认递归包含所有关联层级 def default_includes ['**'] end end
之后在控制器外直接调用RecordSerializer.new(@record).serializable_hash(或者as_json),就能自动递归所有关联层级了。
为什么直接用as_json不行?
简单说,as_json方法在序列化器里是基于serializable_hash实现的,但它默认不会把你初始化时传入的include选项传递给嵌套的子序列化器。而serializable_hash在接收include参数时,会遍历所有关联,把这个参数传递给对应的子序列化器,从而实现完整的递归序列化。
你可以测试一下,用serializable_hash(include: ['**'])得到的结果和控制器里render输出的完全一致,包括那层grandchild的嵌套数据。
内容的提问来源于stack exchange,提问作者Jacquen

