9

我有一個現有的Rails 3應用程序,我添加了一個JSON API。我們有一個Vendor ActiveRecord模型和一個Employee ActiveRecord模型。 Employee屬於Vendor。在API中,我們希望在JSON序列化中包含EmployeeVendor。例如:在Rails JSON序列化中重命名模型關聯屬性?

# 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-y的方式來做我想做的事。有任何想法嗎?

回答

11

試圖用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 
+0

這就是我害怕的...... – thefugal 2010-12-17 01:28:29

0

這是一個偷偷摸摸的做法。創建一個名爲商業模式映射到供應商表:上Employee在「虛擬」協會通過作爲一個選項

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

電話to_json

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

現在添加belongs_to的對員工

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

是的,我以前沒有想到過,那很狡猾。儘管如此,我並不想爲相同的數據創建2個獨立的模型。例如,似乎創建新業務太容易了,並且錯過了我擁有的任何供應商驗證。 – thefugal 2010-12-17 01:27:59

0

我遇到了同樣的問題,所以我修改了as_json也運行遞歸方法來掃描選項,並切換th e關聯的別名的名稱。無論如何,它適用於我的應用程序,可能會爲你的工作。

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