Rails升级后如何恢复jsonb字段自动存JSON字符串为Hash的行为
Rails向PostgreSQL写入jsonb字段的格式化处理环节
Rails对PostgreSQL jsonb字段的待写入数据格式化,发生在Active Record类型序列化层,具体由PostgreSQL适配器内置的ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb类型处理器负责,它的serialize方法会在SQL语句生成、数据发送至数据库前完成最终格式转换,是jsonb数据落库前的最后一道格式化逻辑。
新旧版本行为差异原因
- Rails 5.1及更早版本的jsonb类型处理器,会对传入的JSON字符串自动执行
JSON.parse转换为Hash/Array结构化数据后再存储,无论传入的是原生Hash对象,还是object.to_json生成的JSON字符串,最终落库的都是结构化JSON值,读取时自然返回Hash类型,匹配原有业务逻辑预期。 - Rails 5.2版本起调整了该逻辑:为了支持jsonb字段存储JSON标量值(纯字符串、数字、布尔值等),处理器不再自动解析传入的JSON字符串,传入字符串时会直接将字符串作为值存入jsonb字段,最终读取时拿到的就是String类型而非Hash,触发业务异常。
全局回退旧版存储行为的方案
由于业务中没有jsonb字段存储纯字符串的需求,不需要逐行排查修改代码,可通过初始化补丁全局恢复自动转Hash的逻辑:
- 在项目
config/initializers目录下新建补丁文件ar_jsonb_legacy_serialize.rb - 写入以下代码:
module ActiveRecord module ConnectionAdapters module PostgreSQL module OID # 处理jsonb字段 class Jsonb alias_method :origin_serialize, :serialize def serialize(value) parsed = value.is_a?(String) ? JSON.parse(value) : value origin_serialize(parsed) rescue JSON::ParserError origin_serialize(value) end end # 如果项目中存在普通json类型字段,同步添加补丁 class Json alias_method :origin_serialize, :serialize def serialize(value) parsed = value.is_a?(String) ? JSON.parse(value) : value origin_serialize(parsed) rescue JSON::ParserError origin_serialize(value) end end end end end end
补丁添加完成后重启Rails服务即可生效,所有传入jsonb/json字段的JSON字符串都会自动解析为结构化的Hash/Array后再存储,完全对齐旧版本行为。如果后续个别字段需要存储纯字符串,单独为对应字段自定义类型转换器即可,不影响全局逻辑。
内容的提问来源于stack exchange,提问作者KingdomGnark
相关产品推荐
相关产品推荐

