在 Rails JSON 序列化中重命名模型关联属性?

Posted

技术标签:

【中文标题】在 Rails JSON 序列化中重命名模型关联属性?【英文标题】:Renaming model association attributes in Rails JSON serialization? 【发布时间】:2011-05-26 05:33:54 【问题描述】:

我有一个现有的 Rails 3 应用程序,我正在向其中添加一个 JSON API。我们有一个Vendor ActiveRecord 模型和一个Employee ActiveRecord 模型。 Employee 属于 Vendor。在 API 中,我们希望在 JSON 序列化中包含 EmployeeVendor。例如:

# Employee as JSON, including Vendor
:employee => 
    # ... employee attributes ...
    :vendor => 
        # ... vendor attributes ...
    

这很容易。但是,我有一个业务要求,即公共 API 不公开内部模型名称。也就是说,在外界看来,Vendor 模型实际上需要被称为Business

# Employee as JSON, including Vendor as Business
:employee => 
    # ... employee attributes ...
    :business => 
        # ... vendor attributes ...
    

这对于***对象很容易做到。也就是说,我可以调用@employee.as_json(:root => :dude_who_works_here) 将JSON 中的Employee 重命名为DudeWhoWorksHere。但是对于包含的关联呢?我尝试了一些没有成功的事情:

# :as in the association doesn't work
@employee.as_json(:include => :vendor => :as => :business)

# :root in the association doesn't work
@employee.as_json(:include => :vendor => :root => :business)

# Overriding Vendor's as_json doesn't work (at least, not in a association)
    # ... (in vendor.rb)
    def as_json(options)
        super(options.merge(:root => :business))
    end
    # ... elsewhere
    @employee.as_json(:include => :vendor)

我唯一的另一个想法是手动重命名密钥,如下所示:

# In employee.rb
def as_json(options)
    json = super(options)
    if json.key?(:vendor)
        json[:business] = json[:vendor]
        json.delete(:vendor)
    end
    return json
end

但这似乎不优雅。我希望有一种更清洁、更符合 Rails 的方式来做我想做的事。有什么想法吗?

【问题讨论】:

【参考方案1】:

尝试使用 as_json 的内置选项生成复杂的 JSON 很笨拙。我会在你的模型中覆盖as_json,而根本不用打电话给super。为as_json 制作您自己的选项键,以控制您要包含在哈希中的内容。

# employee.rb
def as_json(options = )
  json = :name => name, ... # whatever info you want to expose
  json[:business] = vendor.as_json(options) if options[:include_vendor]
  json
end

【讨论】:

【参考方案2】:

这是一种偷偷摸摸的做法。创建一个名为 Business 的模型映射到 vendor 表:

class Business < ActiveRecord::Base
  set_table_name "vendors"
end

现在在 Employee 上添加一个 belongs_to:

class Employee < ActiveRecord::Base
  belongs_to :vendor
  belongs_to :business, :foreign_key => :vendor_id
  ...
end

Employee 上调用to_json,作为选项传入“虚拟”关联:

 Employee.first.to_json(:only=>:name,:include=>:business)
 # "\"employee\":\"name\":\"Curly\",\"business\":\"name\":\"Moe's Garage\"" 

【讨论】:

是的,我之前没想到,这很偷偷摸摸。不过,我真的不想为相同的数据使用 2 个单独的模型。例如,创建新业务并错过我拥有的任何供应商验证似乎太容易了。【参考方案3】:

我遇到了同样的问题,所以我修改了as_json 以运行递归方法来扫描选项,并将关联名称切换为别名。无论如何,它适用于我的应用程序,可能适用于您的应用程序。

def self.test_serializer
  as_json(
    
      :include => 
        :contact_info => as: :contact_info_attributes
      
    
  )
end

def as_json(options=)
  data = super(options)
  data = as_json_associations_alias_fix(options, data)
  return data
end

def as_json_associations_alias_fix options, data, opts = 
  if options[:include].present?
    options[:include].each do |include_key, include_hash|
      data_key = include_key.to_s
      if include_hash[:as].present?
        alias_name = include_hash[:as]
        data[alias_name.to_s] = data[include_key.to_s]# if !data.is_a?(Array)
        data.delete(include_key.to_s)# if !data.is_a?(Array)
        data_key = alias_name.to_s
      end
      # Handle to-one association here, else handle the to-many association.
      if !data[data_key].is_a?(Array)
        data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key])
      else
        data[data_key].each_with_index do |value,i|
          data[i] = as_json_associations_alias_fix(include_hash, value)
        end
      end
    end
  end
  return data
end

【讨论】:

以上是关于在 Rails JSON 序列化中重命名模型关联属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 nest.js 中重命名 JSON 字段

命名空间模型中的 Rails 关联

列出存在于另一个模型中的所有关联模型记录,该模型存在于 rails 中的另一个命名空间中

如何在Bash中重命名关联数组?

Rails 仅将模型嵌套关联中的某些字段呈现为 json

Rails 隔离引擎模型关联