如何在Rails中构建由多个模型组成的JSON响应

首先,期望的结果

我有UserItem模型。 我想构建一个如下所示的JSON响应:

 { "user": {"username":"Bob!","foo":"whatever","bar":"hello!"}, "items": [ {"id":1, "name":"one", "zim":"planet", "gir":"earth"}, {"id":2, "name":"two", "zim":"planet", "gir":"mars"} ] } 

但是,我的UserItem模型具有的属性不仅仅是那些。 我找到了一种方法让它起作用,但要注意 ,它不漂亮……请帮助……

更新

下一节包含原始问题。 最后一节显示了新的解决方案。


我的黑客

home_controller.rb

 class HomeController  Observation.new(current_user, @items).to_json } end end end 

observation.rb

 # NOTE: this is not a subclass of ActiveRecord::Base # this class just serves as a container to aggregate all "observable" objects class Observation attr_accessor :user, :items def initialize(user, items) self.user = user self.items = items end # The JSON needs to be decoded before it's sent to the `to_json` method in the home_controller otherwise the JSON will be escaped... # What a mess! def to_json { :user => ActiveSupport::JSON.decode(user.to_json(:only => :username, :methods => [:foo, :bar])), :items => ActiveSupport::JSON.decode(auctions.to_json(:only => [:id, :name], :methods => [:zim, :gir])) } end end 

看马! 没有更多的黑客!

改为覆盖as_json

ActiveRecord :: Serialization#as_json文档非常稀疏。 这是简要说明:

 as_json(options = nil) [show source] 

有关to_json vs as_json更多信息,请参阅Rails 2.3.5中覆盖to_json的已接受答案

代码没有黑客攻击

user.rb

 class User  [:username], :methods => [:foo, :bar] }.merge(options) super(options) end end 

item.rb的

 class Item  [:id, name], :methods => [:zim, :gir] }.merge(options) super(options) end end 

home_controller.rb

 class HomeController  { :user => current_user || {}, :items => @items } end end end end 

编辑使用as_json而不是to_json 。 请参阅如何在Rails中覆盖to_json? 详细解释。 我认为这是最好的答案。

您可以在控制器中呈现所需的JSON,而无需辅助模型。

 def observe respond_to do |format| format.js do render :json => { :user => current_user.as_json(:only => [:username], :methods => [:foo, :bar]), :items => @items.collect{ |i| i.as_json(:only => [:id, :name], :methods => [:zim, :gir]) } } end end end 

确保ActiveRecord::Base.include_root_in_json设置为false,否则您将在’user’中获得’user’属性。 不幸的是,看起来Arrays不会将options传递给每个元素,因此collect是必要的。

任何人都在为此寻找替代解决方案,这就是我在Rails 4.2中解决这个问题的方法:

 def observe @item = some_item @user = some_user respond_to do |format| format.js do serialized_item = ItemSerializer.new(@item).attributes serialized_user = UserSerializer.new(@user).attributes render :json => { :item => serialized_item, :user => serialized_user } end end end 

这将两个对象的序列化版本作为JSON返回,可通过response.userresponse.item访问。

现在有很多新的Gems用于构建JSON,对于这种情况,我发现最合适的是Jsonify:

https://github.com/bsiggelkow/jsonify https://github.com/bsiggelkow/jsonify-rails

这允许您从模型中构建属性和数组的混合。

工作答案#2为避免json被“转义”的问题,请手动构建数据结构,然后在其上调用to_json 一次 。 它可以有点罗嗦,但你可以在控制器中完成所有操作,或者将它抽象为各个模型,如to_hash或其他东西。

 def observe respond_to do |format| format.js do render :json => { :user => {:username => current_user.username, :foo => current_user.foo, :bar => current_user.bar}, :items => @items.collect{ |i| {:id => i.id, :name => i.name, :zim => i.zim, :gir => i.gir} } } end end end