Ruby on rails 将虚拟属性添加到json输出

Ruby on rails 将虚拟属性添加到json输出,ruby-on-rails,json,Ruby On Rails,Json,假设我有一个处理待办事项列表的应用程序。该列表包含已完成和未完成的项目。现在我想向列表对象添加两个虚拟属性;列表中已完成和未完成项目的计数。我还需要在json输出中显示这些内容 我的模型中有两种方法用于获取未完成/已完成的项目: def unfinished_items self.items.where("status = ?", false) end def finished_items self.items.where("status = ?", true) end 那

假设我有一个处理待办事项列表的应用程序。该列表包含已完成和未完成的项目。现在我想向列表对象添加两个虚拟属性;列表中已完成和未完成项目的计数。我还需要在json输出中显示这些内容

我的模型中有两种方法用于获取未完成/已完成的项目:

def unfinished_items 
  self.items.where("status = ?", false) 
end 

def finished_items 
  self.items.where("status = ?", true) 
end
那么,如何在json输出中获得这两个方法的计数呢


我正在使用Rails 3.1,Rails中对象的序列化有两个步骤:

  • 首先,调用
    as_json
    将对象转换为简化的散列
  • 然后,在
    as_json
    返回值上调用
    to_json
    ,以获取最终的json字符串
您通常希望将
单独留给_json
,因此您所需要做的就是添加如下内容:

def as_json(options = { })
  # just in case someone says as_json(nil) and bypasses
  # our default...
  super((options || { }).merge({
    :methods => [:finished_items, :unfinished_items]
  }))
end
def as_json(options = { })
  h = super(options)
  h[:finished]   = finished_items
  h[:unfinished] = unfinished_items
  h
end
您也可以这样做:

def as_json(options = { })
  # just in case someone says as_json(nil) and bypasses
  # our default...
  super((options || { }).merge({
    :methods => [:finished_items, :unfinished_items]
  }))
end
def as_json(options = { })
  h = super(options)
  h[:finished]   = finished_items
  h[:unfinished] = unfinished_items
  h
end
如果要为方法支持的值使用不同的名称


如果您关心XML和JSON,请查看。

另一种方法是将其添加到您的模型中:

def attributes
  super.merge({'unfinished' => unfinished_items, 'finished' => finished_items})
end
这也将自动适用于xml序列化。
但是请注意,您可能希望使用字符串作为键,因为在rails 3中对键进行排序时,该方法无法处理符号。但是它在rails 4中没有排序,所以应该不会再有问题了。

对于rails 4,您可以执行以下操作-

render json: @my_object.to_json(:methods => [:finished_items, :unfinished_items])

希望这有助于更新/最新版本的用户

我只是想为像我这样的人提供这个答案,他们试图将此集成到现有的as_json块中:

  def as_json(options={})
    super(:only => [:id, :longitude, :latitude],
          :include => {
            :users => {:only => [:id]}
          }
    ).merge({:premium => premium?})

只需将
.merge({})
添加到
super()的末尾
只需将所有数据关闭为一个散列,如


render json:{items:items,finished:finished,unfinished:unfinished}

这就可以了,而不必做一些难看的重写。例如,如果您有一个型号
列表
,您可以将其放入控制器中:

  render json: list.attributes.merge({
                                       finished_items: list.finished_items,
                                       unfinished_items: list.unfinished_items
                                     })
如上所述,
:methods
将允许您将特定模型的方法/函数作为json属性返回,如果您有复杂的关联,这将起到作用,因为它将向现有模型/关联添加函数:D如果您不想将
重新定义为\u json

检查此代码,请注意我是如何使用
:methods
以及
:include
[N+查询甚至不是一个选项;)]

render json:@YOUR_MODEL.to_json(:methods=>[:method_1,:method_2],:include=>[:company,:surveys,:customer=>{:include=>[:user]}])

在这种情况下,将
改写为_json
函数将更加困难(特别是因为您必须手动添加
:include
关联:/

def as_json(选项={})
结束

如果要渲染具有虚拟属性的对象数组,可以使用

render json: many_users.as_json(methods: [:first_name, :last_name])

其中
first_name
last_name
是在您的模型上定义的虚拟属性

我没有尝试过这一点:也许您所要做的就是添加属性读取器:finished_items?我在最上面的一个错误中得到了这个错误:nil:NilClass的未定义方法
merge',因此我将其替换为
选项。merge`with
(选项|{}).merge
@Turadg:由于默认的
选项={},第一个应该可以工作
除非有人在某处传递一个显式的
nil
,否则为了安全起见,我将修补额外的偏执狂。抱歉,您的评论在每天的混乱中丢失了,所以我回您电话有点晚。这也是确保在呈现关联对象和使用
include: (我用它来解决使用attr_加密gem的问题)