【问题标题】:Renaming model association attributes in Rails JSON serialization?在 Rails JSON 序列化中重命名模型关联属性?
【发布时间】: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 的方式来做我想做的事。有什么想法吗?

【问题讨论】:

    标签: ruby-on-rails json serialization activerecord ruby-on-rails-3


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-20
        • 2018-07-07
        • 1970-01-01
        • 2013-05-28
        • 2015-08-25
        • 1970-01-01
        相关资源
        最近更新 更多