在 postgresql 中处理 Unicode 序列

问题描述 投票:0回答:6

我在 postgresql 数据库 (9.4.1) 的 JSON(不是 JSONB)列中存储了一些 JSON 数据。其中一些 JSON 结构在其属性值中包含 unicode 序列。例如:

{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }

当我尝试查询此 JSON 列时(即使我没有直接尝试访问

device_name
属性),我收到以下错误:

错误:不支持的 Unicode 转义序列
详细:

\u0000
无法转换为文本。

您可以通过在 postgresql 服务器上执行以下命令来重新创建此错误:

select '{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }'::json->>'client_id'

这个错误对我来说是有意义的 - 根本没有办法在文本结果中表示 unicode 序列

NULL

有什么方法可以让我查询相同的 JSON 数据,而不必对传入数据执行“清理”?这些 JSON 结构会定期更改,因此扫描特定属性(在本例中为

device_name
)并不是一个好的解决方案,因为很可能存在其他可能保存类似数据的属性。


经过更多调查,似乎此行为是版本 9.4.1 中的新行为,如更改日志中提到的

...因此,当需要转换为反转义形式时,现在 json 值中的

\u0000
也将被拒绝。只要不对值进行任何处理,此更改就不会破坏在 json 列中存储
\u0000
的能力...

这真的是故意的吗?降级到 9.4.1 之前的版本是可行的选择吗?


附带说明一下,此属性取自客户端移动设备的名称 - 是用户将此文本输入到设备中。用户到底是如何插入

NULL
REPLACEMENT CHARACTER
值的?!

json postgresql unicode
6个回答
64
投票

\u0000
是在字符串中无效的一个 Unicode 代码点。除了清理字符串之外,我没有其他方法。

由于

json
只是特定格式的字符串,因此您可以使用标准字符串函数,而不必担心 JSON 结构。删除代码点的单行清理程序是:

SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;

但是您也可以插入您喜欢的任何字符,如果将零代码点用作某种形式的分隔符,这将很有用。

还要注意数据库中存储的内容和向用户呈现的方式之间的细微差别。您可以将代码点存储在 JSON 字符串中,但在将值处理为

json
数据类型之前,必须将其预处理为其他字符。


4
投票

我找到了适合我的解决方案

SELECT (regexp_replace(the_string::text, '(?<!\\)\\u0000', '', 'g'))::json;
注意匹配模式 

'(?.


2
投票
帕特里克的解决方案对我来说并不是开箱即用的。不管怎样,总是会抛出错误。然后我进行了更多研究,并能够编写一个小的自定义函数来为我解决问题。

首先我可以通过写来重现错误:

select json '{ "a": "null \u0000 escape" }' ->> 'a' as fails

然后我添加了一个在查询中使用的自定义函数:

CREATE OR REPLACE FUNCTION null_if_invalid_string(json_input JSON, record_id UUID) RETURNS JSON AS $$ DECLARE json_value JSON DEFAULT NULL; BEGIN BEGIN json_value := json_input ->> 'location'; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'Invalid json value: "%". Returning NULL.', record_id; RETURN NULL; END; RETURN json_input; END; $$ LANGUAGE plpgsql;

要调用该函数,请执行以下操作。您不应该收到错误。

select null_if_invalid_string('{ "a": "null \u0000 escape" }', id) from my_table

这应该按预期返回 json:

select null_if_invalid_string('{ "a": "null" }', id) from my_table
    

2
投票
仅适用于滞留在此处的网络搜索者:

这不是确切问题的解决方案,但在某些类似情况下,如果您只是

不希望 json 中包含空字节的数据集,那么这是解决方案。只需添加:

AND json NOT LIKE '%\u0000%'
在 WHERE 语句中。

您还可以使用 REPLACE SQL 语法来清理数据:

REPLACE(source_field, '\u0000', '' );
    

2
投票
您可以像这样使用 SQL 修复所有条目:

update ___MY_TABLE___ set settings = REPLACE(settings::text, '\u0000', '' )::json where settings::text like '%\u0000%'
    

0
投票
我在从 Postgres 数据库中提取 JSON 数据时遇到了这个问题,@Patrick 的答案在 PgAdmin 中完美运行,但我发现它在使用

psycopg2

 的 Python 代码中出错。我无法逃脱 
\n0000
 中的反斜杠。
解决方案是使用足够的转义字符来满足 Postgres 以及 Python 解释器的要求。我总共需要使用四 (4) 个反斜杠来转义

\n0000

:

\\\\n0000

如果这在 Python 中的 SQL 查询字符串中仍然不起作用,您可能必须对其进行参数化,例如:

query = sql.SQL("""
           select (regexp_replace(data::text, %s, '', 'g'))::json...
        """)

...
....

cursor.execute(query, ('\\\\u0000',))

© www.soinside.com 2019 - 2024. All rights reserved.