我的 Rails 6.1.1 应用程序中的
jsonb
模型有一个名为“有效负载”的 Tweet
类型列。我使用 store 直接访问此属性上的各个字段:
class Tweet < ApplicationRecord
store :payload, accessors: [:lang, :text, :entities], coder: JSON
end
(请注意,将序列化从 YAML 默认值更改为 JSON 需要
coder: JSON
部分 - 否则您最终会在 jsonb
列中看到 YAML。)
当我创建新推文时,我发现 ActiveRecord 在插入过程中错误地转义了 JSON 字符串:
Tweet.create payload: {foo: 'bar'}
TRANSACTION (0.7ms) BEGIN
Tweet Create (2.0ms) INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id" [["payload", "\"{\\\"foo\\\":\\\"bar\\\"}\""], ...]
我指的是
"\"{\\\"foo\\\":\\\"bar\\\"}\""
部分。看起来已经被双重逃脱了。这会导致一种“stringception”,其中字符串中的字符串存储在 payloads
列中,使得 postgres 无法使用箭头 ->
语法对字段执行任何搜索。 Postgres 无法将此值识别为 JSON。 (然而,看似奇迹般的是,Rails 能够在读取操作时正确反序列化该字段。)
另一位 SO 用户已说明此处的问题:https://dbfiddle.uk/gcwTQOUm
当我不使用
store
时,我无法重现该问题:
Tweet.create payload: {foo: 'bar'}
TRANSACTION (0.2ms) BEGIN
Tweet Create (1.6ms) INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id" [["payload", "{\"foo\":\"bar\"}"], ...]
"{\"foo\":\"bar\"}"
是所需的字符串。
这让我相信我使用
store
是错误的。
我检查了文档:
注意:如果您使用结构化数据库数据类型(例如 PostgreSQL
/hstore
或 MySQL 5.7+json
),则不需要 .store 提供的序列化。只需使用 .store_accessor 来生成访问器方法。请注意,这些列使用字符串键控哈希,并且不允许使用符号进行访问。json
换句话说,因为我已经使用了
jsonb
类型的列,Rails 已经知道要序列化哈希 - 应用另一个序列化会导致双重转义。
所以,不要这样做:
store :payload, accessors: [:lang, :text, :entities], coder: JSON
我现在做的是:
store_accessor :payload, :lang, :text, :entities
而且它有效。