【问题标题】:Add virtual attribute to json output将虚拟属性添加到 json 输出
【发布时间】:2011-07-31 20:16:34
【问题描述】:

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

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

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

def finished_items 
  self.items.where("status = ?", true) 
end

那么,如何在我的 json 输出中获取这两种方法的计数?

我正在使用 Rails 3.1

【问题讨论】:

  • 我还没有尝试过:也许你所要做的就是添加一个 attr_reader :finished_items ?

标签: ruby-on-rails json


【解决方案1】:

Rails中对象的序列化有两个步骤:

  • 首先,调用as_json 将对象转换为简化的哈希。
  • 然后,在as_json 返回值上调用to_json 以获取最终的JSON 字符串。

你通常想单独留下to_json,所以你需要做的就是添加your own as_json implementation,就像这样:

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,请查看 serializable_hash

【讨论】:

  • 我得到了最上面的错误:未定义的方法merge' for nil:NilClass So I replaced options.merge` 和(options||{}).merge
  • @Turadg:由于默认的options = { },第一个应该可以工作,除非有人在某个地方传递了明确的nil,为了安全起见,我会修补额外的偏执狂。抱歉,你的评论在日常的混乱中丢失了,所以我回复你有点晚了。
【解决方案2】:

使用 Rails 4,您可以执行以下操作 -

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

希望这对使用最新/最新版本的人有所帮助

【讨论】:

    【解决方案3】:

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

    def attributes
      super.merge({'unfinished' => unfinished_items, 'finished' => finished_items})
    end
    

    这也将自动适用于 xml 序列化。 http://api.rubyonrails.org/classes/ActiveModel/Serialization.html 但请注意,您可能希望对键使用字符串,因为该方法在对 rails 3 中的键进行排序时无法处理符号。但它没有在 rails 4 中排序,因此应该不再有问题。

    【讨论】:

    • 这也是确保在渲染关联对象和使用 include: 时包含虚拟属性的好方法(我用它来解决使用 attr_encrypted gem 的问题)
    【解决方案4】:

    只需将所有数据合并到一个哈希中,例如

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

    【讨论】:

      【解决方案5】:

      我只是想为像我这样试图将其集成到现有 as_json 块中的任何人提供这个答案:

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

      只需将 .merge({}) 添加到您的 super() 末尾

      【讨论】:

        【解决方案6】:

        这样就可以了,而不必做一些丑陋的覆盖。例如,如果你有一个模型 List,你可以把它放在你的控制器中:

          render json: list.attributes.merge({
                                               finished_items: list.finished_items,
                                               unfinished_items: list.unfinished_items
                                             })
        

        【讨论】:

          【解决方案7】:

          正如上面列出的Aswin:methods 将使您能够将特定模型的方法/函数作为 json 属性返回,如果您有复杂的关联,这将起到作用,因为它会将函数添加到现有模型/ assossiations :D 如果您不想重新定义as_json,它会像魅力一样发挥作用@

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

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

          在这种情况下,覆盖as_json 函数将更加困难(特别是因为您必须手动添加:include 关联:/ def as_json(options = { }) end

          【讨论】:

            【解决方案8】:

            如果你想渲染一个带有虚拟属性的对象数组,你可以使用

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

            其中first_namelast_name 是模型上定义的虚拟属性

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-06-13
              • 2011-06-26
              • 1970-01-01
              • 2019-12-31
              • 1970-01-01
              • 2011-05-08
              • 2019-09-17
              相关资源
              最近更新 更多