9

Ich habe eine vorhandene Rails 3-Anwendung, zu der ich eine JSON-API hinzufüge. Wir haben ein ActiveRecord-Modell Vendor und ein ActiveRecord-Modell Employee. Ein Employee gehört zu einem Vendor. In der API möchten wir eine EmployeeVendor in der JSON-Serialisierung enthalten. Zum Beispiel:Modellzuordnungsattribute in Rails JSON-Serialisierung umbenennen?

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

Das ist einfach genug. Ich habe jedoch eine Geschäftsanforderung, dass die öffentliche API die internen Modellnamen nicht verfügbar macht. Das heißt, mit der Außenwelt, muss es so scheinen, obwohl das Vendor Modell tatsächlich Business genannt:

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

Dies ist einfach für das Top-Level-Objekt zu tun. Das heißt, ich könnte @employee.as_json(:root => :dude_who_works_here) anrufen, um Employee zu DudeWhoWorksHere im JSON umzubenennen. Aber was ist mit den eingeschlossenen Verbänden? Ich habe ein paar Dinge ohne Erfolg versucht:

# :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) 

Die einzige andere Idee, die ich habe, ist, um manuell den Schlüssel umzubenennen, so etwas wie dieses:

# 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 

Aber das scheint unelegant. Ich hoffe, es gibt eine sauberere, mehr Rails-y-Art zu tun, was ich will. Irgendwelche Ideen?

Antwort

11

Der Versuch, komplexe JSON mit den integrierten Optionen für as_json zu generieren, ist schwerfällig. Ich würde as_json in Ihren Modellen überschreiben, und mich überhaupt nicht mit dem Aufruf super befassen. Erstellen Sie Ihre eigenen Optionsschlüssel für as_json, um zu steuern, was Sie in den Hash aufnehmen möchten.

# 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

Das war, wovor ich Angst hatte ... – thefugal

0

Hier ist eine hinterhältige Art und Weise es zu tun. Erstellen Sie ein Modell abgebildet Geschäft auf die Anbieter-Tabelle genannt:

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

Jetzt eine belongs_to auf Mitarbeiter hinzufügen:

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

Anruf to_json auf Employee in der "virtuellen" Vereinigung vorbei als Option:

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

Yeah, ich hatte vorher nicht daran gedacht, das ist ziemlich hinterhältig. Ich möchte jedoch nicht wirklich zwei getrennte Modelle für die gleichen Daten haben. Scheint so, als wäre es zu einfach, ein neues Geschäft zu erstellen und irgendwelche Vendor Validierungen, die ich habe, zum Beispiel zu verpassen. – thefugal

0

Ich lief in das gleiche Problem, so änderte ich die as_json, um auch eine rekursive Methode zum Scannen durch die Optionen zu starten, und wechseln th Die Namen der Assoziationen für die Aliasnamen. Wie auch immer, es funktioniert für meine App, könnte für Ihre arbeiten.

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