ActiveRecord 错误地转义 JSON 字符串

dch*_*cke 3 activerecord ruby-on-rails

我的 Rails 6.1.1 应用程序中的模型有一个jsonb名为“payloads”的类型列Tweet。我用的是商店直接访问此属性上的各个字段:

\n
class Tweet < ApplicationRecord\n  store :payload, accessors: [:lang, :text, :entities], coder: JSON\nend\n
Run Code Online (Sandbox Code Playgroud)\n

(请注意,该部分coder: JSON是将序列化从 YAML 默认值更改为 JSON \xe2\x80\x93 所必需的,否则最终会在您的jsonb列中看到 YAML。)

\n

当我创建新推文时,我发现 ActiveRecord 在插入过程中错误地转义了 JSON 字符串:

\n
Tweet.create payload: {foo: \'bar\'}\n\nTRANSACTION (0.7ms)  BEGIN\n  Tweet Create (2.0ms)  INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id"  [["payload", "\\"{\\\\\\"foo\\\\\\":\\\\\\"bar\\\\\\"}\\""], ...]\n
Run Code Online (Sandbox Code Playgroud)\n

我指的是部分"\\"{\\\\\\"foo\\\\\\":\\\\\\"bar\\\\\\"}\\""。看起来已经被双重逃脱了。这会导致一种“stringception”,其中字符串中的字符串存储在列中payloads,导致 postgres 无法使用箭头语法对字段执行任何搜索->。Postgres 无法将此值识别为 JSON。(然而,Rails 能够在读取操作时正确反序列化该字段,这似乎是奇迹。)

\n

另一位 SO 用户在这里说明了这个问题:https://dbfiddle.uk/gcwTQOUm

\n

dch*_*cke 5

当我使用时store,我无法重现该问题:

\n
Tweet.create payload: {foo: \'bar\'}\n  TRANSACTION (0.2ms)  BEGIN\n  Tweet Create (1.6ms)  INSERT INTO "tweets" ("payload", ...) VALUES ($1, ...) RETURNING "id"  [["payload", "{\\"foo\\":\\"bar\\"}"], ...]\n
Run Code Online (Sandbox Code Playgroud)\n

"{\\"foo\\":\\"bar\\"}"是所需的字符串。

\n

这让我相信我使用的是store错误的。

\n

我检查了文档

\n
\n

注意:如果您使用结构化数据库数据类型(例如 PostgreSQL hstore/json或 MySQL 5.7+ json),则不需要.store提供的序列化。只需使用.store_accessor来生成访问器方法。请注意,这些列使用字符串键控哈希,并且不允许使用符号进行访问。

\n
\n

换句话说,因为我已经使用了jsonb-type 列,Rails 已经知道序列化哈希 \xe2\x80\x93,应用另一个序列化会导致双重转义。

\n

因此,不要这样做:

\n
store :payload, accessors: [:lang, :text, :entities], coder: JSON\n
Run Code Online (Sandbox Code Playgroud)\n

我现在这样做:

\n
store_accessor :payload, :lang, :text, :entities\n
Run Code Online (Sandbox Code Playgroud)\n

它有效。

\n