Rails返回JSON序列化属性with_indifferent_access

我以前有:

serialize :params, JSON 

但是这将返回JSON并将哈希键符号转换为字符串。 我想使用符号引用哈希,这在使用哈希时最常见。 我给它添加符号,Rails返回字符串。 为了避免这种情况,我创建了自己的getter / setter。 setter很简单(JSON编码),getter是:

  def params read_attribute(:params) || JSON.parse(read_attribute(:params).to_json).with_indifferent_access end 

我无法直接引用params ,因为这会导致循环,所以我使用read_attribute ,现在我的哈希键可以用符号或字符串引用。 但是,这不会更新哈希:

 model.params.merge!(test: 'test') puts model.params # => returns default params without merge 

这让我觉得哈希是由副本引用的。

我的问题是双重的。 我是否可以扩展活动记录JSON序列化以返回无关紧要的访问哈希(或不将符号转换为字符串),并且仍然具有如上所述的哈希工作与合并? 如果没有,我该怎么做才能改善我的吸气剂,以便使用model.params.merge! 作品?

我希望有一些东西(有效):

  def params_merge!(hash) write_attribute(:params, read_attribute(:params).merge(hash)) end # usage: model.params_merge!(test: 'test') 

更好的是,只需让Rails返回一个具有无差别访问权限的哈希值,或者不将我的符号转换为字符串! 感谢任何帮助。

使用内置的serialize方法:

 class Whatever < ActiveRecord::Base serialize :params, HashWithIndifferentAccess end 

有关详细信息,请参阅序列化中的ActiveRecord :: Base文档 。

根据@ fguillen的要求发表评论作为回答……警告:我通常不是一个Rubyist ……所以这可能不是惯用的或有效的。 从function上说,它让我得到了我想要的东西。 似乎在Rails 3.2和4.0中工作……

application_helper.rb

 module ApplicationHelper class JSONWithIndifferentAccess def self.load(str) obj = HashWithIndifferentAccess.new(JSON.load(str)) #...or simply: obj = JSON.load(str, nil, symbolize_names:true) obj.freeze #i also want it set all or nothing, not piecemeal; ymmv obj end def self.dump(obj) JSON.dump(obj) end end end 

在我的模型中,我有一个名为rule_spec的字段,序列化为text字段:

 serialize :rule_spec, ApplicationHelper::JSONWithIndifferentAccess 

最终,我意识到我只想要符号,而不是无关紧要的访问,但通过调整加载方法,您可以获得任一行为。

使用HashWithIndifferentAccess非常棒,但它仍然像Hash一样,并且它只能在数据库中序列化为YAML。

使用Postgres 9.3及更高版本,我的偏好是在Postgres中使用json列类型。 这意味着当读取表时,ActiveRecord将直接从Postgres获取Hash。

 create_table "gadgets" do |t| t.json "info" end 

ActiveRecord serialize要求您为它提供一个类,该类负责读取/写入数据并对其进行序列化/反序列化。

因此,您可以通过inheritanceHashWithIndifferentAccess或我的首选项Hashie::Mash来创建一个完成工作的对象。 然后,您将序列化实现为dumpload类方法。

 class HashieMashStoredAsJson < Hashie::Mash def self.dump(obj) ActiveSupport::JSON.encode(obj.to_h) end def self.load(raw_hash) new(raw_hash || {}) end end 

在模型中,您可以指定此类进行序列化。

 class Gadget < ActiveRecord::Base serialize :info, HashieMashStoredAsJson # This allows the field to be set as a Hash or anything compatible with it. def info=(new_value) self[:info] = HashieMashStoredAsJson.new new_value end end 

如果您不在Postgres中使用json列类型,则实现会略有变化

完整代码和文档: 使用JSON列类型并使用字符串列类型 。

我最终使用了bimsapi解决方案的变体,您不仅可以使用简单的非嵌套JSON,还可以使用任何 JSON。

一旦加载……

 module JsonHelper class JsonWithIndifferentAccess def self.load(str) self.indifferent_access JSON.load(str) end def self.dump(obj) JSON.dump(obj) end private def self.indifferent_access(obj) if obj.is_a? Array obj.map!{|o| self.indifferent_access(o)} elsif obj.is_a? Hash obj.with_indifferent_access else obj end end end end 

然后而不是打电话

 JSON.load(http_response) 

你打电话

 JsonHelper::JsonWithIndifferentAccess.load(http_response) 

同样的事情,但所有嵌套的哈希是无关紧要的访问。

应该为您提供良好的服务,但在将其作为所有解析的默认方法之前要考虑一下,因为大量的JSON有效负载将在本机JSON解析器之上添加重要的ruby操作,该解析器在C中进行了优化,并且更加完全针对性能而设计。