Json 在postgresql中处理Unicode序列

Json 在postgresql中处理Unicode序列,json,postgresql,unicode,Json,Postgresql,Unicode,我在postgresql数据库(9.4.1)的JSON(而不是JSONB)列中存储了一些JSON数据。其中一些JSON结构的属性值中包含unicode序列。例如: {"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" } 当我尝试查询此JSON列时(即使我没有直接尝试访问device\u name属性),我得到以下错误: 错误:不支持的Unicode转义序列 详细信息:\u0000无法转换为文本 通过在po

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

{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }
当我尝试查询此JSON列时(即使我没有直接尝试访问
device\u name
属性),我得到以下错误:

错误:不支持的Unicode转义序列
详细信息:
\u0000
无法转换为文本

通过在postgresql server上执行以下命令,可以重新创建此错误:

select '{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }'::json->>'client_id'
这个错误对我来说很有意义——根本没有办法在文本结果中表示unicode序列
NULL

我有没有办法查询相同的JSON数据而不必对传入数据执行“卫生”操作?这些JSON结构会定期更改,因此扫描特定属性(
device\u name,在本例中为
)将不是一个好的解决方案,因为可能存在其他可能包含类似数据的属性


经过进一步调查,该行为对于9.4.1版来说似乎是新的,如下所示:

…因此,当需要转换为反转义形式时,json值中的
\u0000
现在也将被拒绝。只要不对值进行处理,此更改不会破坏在json列中存储
\u0000
的功能

这是真的吗?将评级下调至9.4.1之前是否可行


作为旁注,此属性取自客户端移动设备的名称-是用户将此文本输入到设备中。用户究竟是如何插入数据和值的?!
\u0000
是字符串中无效的一个Unicode代码点。我看没有别的办法,只能对字符串进行消毒

由于
json
只是一个特定格式的字符串,因此可以使用标准字符串函数,而不用担心json结构。删除代码点的单线消毒剂应为:

SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;
但是您也可以插入您喜欢的任何字符,如果将零代码点用作某种形式的分隔符,这将非常有用


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

Patrick的解决方案对我来说不是现成的。不管怎样,总是会抛出一个错误。然后我进行了更多的研究,能够编写一个小的自定义函数来解决这个问题

首先,我可以通过以下方式重现错误:

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

如果不需要这些空字节结果,只需添加:

AND json NOT LIKE '%\u0000%'

在您的WHERE语句中

感谢您的输入,@patrick。我刚刚对我的帖子进行了编辑。同样的问题不会在3.9.1版中重现,因此在某些阶段,它被视为有效字符串。我不太喜欢对进入我系统的每一条数据都使用如此广泛的正则表达式处理,但如果我手头拮据,没有出路,至少我可以使用它:)所以,谢谢你。是的。空字节在PostgreSQL字符串中不合法。这两者都不是零代码点。@CraigRinger-在字符串响应中不可能只得到一个文本
“\u0000”
?@Lix是的,正如9.4.1的发行说明所指出的,你可以用
\\\\u0000
替换
\\\\u0000
,你会得到“设备名称”的“FooBarï½\u0000ï½\x0Fï½”(在9.4.4上测试),但是您仍然需要
regexp\u替换
初始json。使用9.6测试,您也可以使用
replace
而不是regex版本,例如
select replace('{“a”:“null\u0000 word\u0000 escape”},'\u0000',''::json->“a”成功参考,这不仅是代码< >选择< /代码> -我得到了与SQL <代码>更新<代码>语句相同的问题9.5和96FYI,我运行了<代码>选择< /C> >,您在PostgreSQL 10.1上提供,由VisualC++生成1800编译,32位,它返回了相同的错误。我对Patric solution也有一些问题,但这是因为我在查询中尝试使用它太晚了-您需要尽快修复数据,因为似乎即使是像
json\u array\u elements
这样的操作也会失败。因此,只需将您正在使用的整个json转换为
text
,然后替换并转换回
json
,然后查询就可以工作了。这将过滤掉包含空字节的所有结果。我(大约5年前,呵呵)对不包含空字节的数据本身感兴趣。这几乎是一个如何在不需要清理传入数据的情况下仅排除这些字符的问题。回顾过去,最好是清理数据,确保没有插入无效的json。