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

Ruby on rails 在Rails JSON序列化中重命名模型关联属性?,ruby-on-rails,json,serialization,activerecord,ruby-on-rails-3,Ruby On Rails,Json,Serialization,Activerecord,Ruby On Rails 3,我有一个现有的Rails 3应用程序,我正在向其中添加一个JSON API。我们有一个VendorActiveRecord模型和一个EmployeeActiveRecord模型。员工属于供应商。在API中,我们希望在JSON序列化中包含员工的供应商。例如: # Employee as JSON, including Vendor :employee => { # ... employee attributes ... :vendor => { # ..

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

# Employee as JSON, including Vendor
:employee => {
    # ... employee attributes ...
    :vendor => {
        # ... vendor attributes ...
    }
}
这很容易。但是,我有一个业务需求,即公共API不公开内部模型名称。也就是说,对于外部世界,它需要看起来好像
供应商
模型实际上被称为
业务

# Employee as JSON, including Vendor as Business
:employee => {
    # ... employee attributes ...
    :business => {
        # ... vendor attributes ...
    }
}
对于顶级对象,这很容易做到。也就是说,我可以调用
@employee.as_json(:root=>:dude\u who\u works\u here)
employee
重命名为json中的
dudewhowworkshere
。但对于包含在内的协会呢?我尝试了几件事,但都没有成功:

# :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-y的方式来做我想做的事情。有什么想法吗?

尝试使用as_JSON的内置选项生成复杂的JSON很笨拙。在您的模型中,我会将
重写为_json
,而根本不需要调用
super
。为
创建自己的选项键作为_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

这里有一个偷偷摸摸的方法。创建一个名为Business的模型,映射到供应商表:

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

我遇到了同样的问题,因此我将
修改为_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

是的,我以前没想过,这很鬼鬼祟祟。不过,我真的不想为相同的数据使用两个单独的模型。例如,创建新业务并错过任何供应商验证似乎太容易了。
 Employee.first.to_json(:only=>:name,:include=>:business)
 # "{\"employee\":{\"name\":\"Curly\",\"business\":{\"name\":\"Moe's Garage\"}}}" 
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