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
来创建一个完成工作的对象。 然后,您将序列化实现为dump
和load
类方法。
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中进行了优化,并且更加完全针对性能而设计。