ActiveRecord错误地转义了JSON字符串。

3

我在我的Rails 6.1.1应用程序中的Tweet模型上有一个名为'payloads'的jsonb类型列。我使用store直接访问此属性上的各个字段:

class Tweet < ApplicationRecord
  store :payload, accessors: [:lang, :text, :entities], coder: JSON
end

注意,部分代码coder: JSON是必要的,用以将序列化方式从默认的YAML更改为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\\\"}\""。看起来它被双重转义了。这导致了一种“字符串嵌套”的情况,其中一个字符串被存储在payloads列中,从而使得postgres无法使用箭头->语法搜索字段。Postgres无法将此值识别为JSON。(然而,Rails在读取操作上能够正确地反序列化该字段,似乎是个奇迹。)
另一位SO用户在这里阐述了这个问题:https://dbfiddle.uk/gcwTQOUm

虽然回答自己的问题并分享知识通常是好的,但您可能需要先检查是否存在重复问题。这个问题很可能已经被问过和回答了很多次。不过,我现在有点困惑于寻找一个重复目标。 - max
1
在我发布这篇文章之前,我确实检查过是否有重复,并且刚刚又检查了一次。虽然不是彻底的检查,但我还是尽了合理的努力。我刚刚发现了您的这个答案,它提到了我遇到的同样问题(“双重转换”),并提供了相同的解决方案,但相关的问题从一个不同的、无关的角度来看待它。 - Dennis Hackethal
1个回答

5
当我不使用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/json,或 MySQL 5.7+ 的json),则无需使用.store提供的序列化。只需使用.store_accessor生成访问器方法即可。请注意,这些列使用字符串键哈希,不允许使用符号进行访问。

换句话说,因为我已经在使用jsonb类型的列,Rails已经知道要对哈希进行序列化 - 再次进行序列化会导致双重转义。

所以,不要这样做:

"
store :payload, accessors: [:lang, :text, :entities], coder: JSON

我现在做的是:
store_accessor :payload, :lang, :text, :entities

它有效。


2
你可能会对更强大的 jsonb_accessor 感兴趣。 - Schwern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接