我有一些JSON数据存储在postgresql数据库(9.4.1)的JSON(不是JSONB)列中。其中一些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来说是新的mentioned in the changelog:
...因此,当需要转换为转义表单时,{j}值中的
\u0000
也会被拒绝。只要没有对值进行处理,此更改不会破坏将\u0000
存储在json列中的能力...
这真的是意图吗?在这里降级到9.4.1之前是一个可行的选择吗?
<子>
作为旁注,此属性取自客户端移动设备的名称 - 它是将此文本输入设备的用户。用户如何插入NULL
和REPLACEMENT CHARACTER
值?!
子>
答案 0 :(得分:23)
\u0000
是一个在字符串中无效的Unicode代码点。除了消毒字符串之外别无他法。
由于json
只是特定格式的字符串,因此您可以使用标准字符串函数,而无需担心JSON结构。删除代码点的单行清理程序将是:
SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;
但是你也可以插入你喜欢的任何字符,如果将零代码点用作某种形式的分隔符,这将非常有用。
还要注意数据库中存储的内容与呈现给用户的方式之间的细微差别。您可以将代码点存储在JSON字符串中,但在将值作为json
数据类型处理之前,必须先将其预处理为其他字符。
答案 1 :(得分:0)
帕特里克的解决方案并没有为我开箱即用。无论总是抛出一个错误。然后我研究了一点,并且能够编写一个小的自定义函数来解决这个问题。
首先,我可以通过写下来重现错误:
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 :(得分:-1)
如果您不希望这些空字节结果,只需添加:
AND json NOT LIKE '%\u0000%'
在您的WHERE语句中