在 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 序列化中包含 Employee
的 Vendor
。例如:
# 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 序列化中重命名模型关联属性?的主要内容,如果未能解决你的问题,请参考以下文章