Rails中ActiveRecord错误转义JSON字符串问题求助
问题场景
在Rails 6.1.1应用中,Tweet模型的payload字段为jsonb类型,使用ActiveRecord Store直接访问字段属性:
class Tweet < ApplicationRecord store :payload, accessors: [:lang, :text, :entities], coder: JSON end
添加coder: JSON是为了避免默认YAML序列化导致jsonb列存储YAML格式数据,但创建记录时出现双重转义问题:
Tweet.create payload: {foo: 'bar'}
对应的SQL日志显示payload值被处理为"\"{\\\"foo\\\":\\\"bar\\\"}\"",导致数据库中存储的是字符串嵌套字符串的形式,PostgreSQL无法使用->等JSON查询语法操作该字段,不过Rails读取记录时能正常反序列化。
解决方案
这个问题的核心是ActiveRecord Store的coder序列化与PostgreSQL对jsonb字段的原生序列化发生冲突,导致JSON被两次编码。推荐以下两种解决方法:
方法一:移除Store的coder配置(推荐)
PostgreSQL的jsonb字段本身会自动处理哈希与JSON格式的转换,不需要Store额外做JSON编码。修改模型代码:
class Tweet < ApplicationRecord store :payload, accessors: [:lang, :text, :entities] end
修改后执行创建操作,SQL日志中的payload值会是标准JSON格式{"foo":"bar"},数据库存储的是原生JSON对象,既可以通过Store的accessors直接访问属性,也能正常使用PostgreSQL的JSON查询语法(如Tweet.where("payload ->> 'foo' = ?", 'bar'))。
方法二:自定义coder避免双重编码
如果因特殊需求必须保留coder配置,可以自定义序列化逻辑,确保只进行一次JSON编码:
class JsonCoder def self.dump(obj) obj.is_a?(String) ? obj : obj.to_json end def self.load(str) str.is_a?(Hash) ? str : JSON.parse(str) if str.present? end end class Tweet < ApplicationRecord store :payload, accessors: [:lang, :text, :entities], coder: JsonCoder end
自定义的JsonCoder会判断输入类型,避免重复编码,同样能解决双重转义问题。
内容的提问来源于stack exchange,提问作者Dennis Hackethal

