2015-07-28 2 views
17

У меня есть данные JSON, хранящиеся в столбце JSON (не JSONB) в моей базе данных postgresql (9.4.1). Некоторые из этих структур JSON содержат последовательности Unicode в их значениях атрибутов. Например:Обработка последовательностей Unicode в postgresql

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

При попытке запросить этот столбец JSON (даже если я непосредственно не пытается получить доступ к атрибуту device_name), я получаю следующее сообщение об ошибке:

ERROR: unsupported Unicode escape sequence
Detail: \u0000 cannot be converted to text.

Вы можете воссоздать эта ошибка, выполнив следующую команду на сервере PostGreSQL:

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

ошибка имеет смысл для меня - там просто нет способа, чтобы представить последовательность Юникода NULL в текстовом виде.

Можно ли мне запросить одни и те же данные JSON без необходимости выполнять «санитарию» по входящим данным? Эти структуры JSON меняются регулярно, поэтому сканирование определенного атрибута (device_name в этом случае) не будет хорошим решением, поскольку могут быть другие атрибуты, которые могут содержать похожие данные.


После еще нескольких исследований, кажется, что это поведение является новым для версии 9.4.1, как mentioned in the changelog:

...Therefore \u0000 will now also be rejected in json values when conversion to de-escaped form is required. This change does not break the ability to store \u0000 in json columns so long as no processing is done on the values...

Было ли это действительно намерение? Является ли понижение до 9.4.1 жизнеспособным вариантом здесь?


Как примечание стороны, это свойство берется от имени мобильного устройства клиента - это пользователь, который вошел в этот текст в устройство. Как на самом деле пользователь ввел NULL и REPLACEMENT CHARACTER значения ?!

+0

Для справки, это не только случай с 'SELECT' - я получаю такую ​​же проблему с SQL' UPDATE' заявление на 9.5 и 9.6. –

ответ

14

\u0000 - это единственная точка кода Unicode, которая недопустима в строке. Я не вижу другого способа, кроме как санировать струну.

С json это просто строка в определенном формате, вы можете использовать стандартные строковые функции, не беспокоясь о структуре JSON. Однострочный дезинфицирующее, чтобы удалить точку коды будет:

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

Но вы также можете вставить любой характер вашей души, что было бы полезно, если точка нулевого кода используется в качестве какой-либо формы разделителя.

Обратите также внимание на тонкую разницу между тем, что хранится в базе данных и как она представлена ​​пользователю. Вы можете сохранить точку кода в строке JSON, но перед обработкой значения в качестве типа данных json вы должны предварительно обработать его каким-либо другим символом.

+0

Спасибо за ваш вклад, @patrick. Я только что редактировал свой пост. Эта же проблема не воспроизводится в версии 3.9.1, поэтому в * некоторой стадии * она * считалась допустимой строкой. Я не слишком доволен использованием такой широкой обработки регулярных выражений для каждой части данных, которая входит в мою систему, но по крайней мере я смогу использовать ее, если я нахожусь в узком месте и не имею выхода. Итак, Спасибо. – Lix

+0

Да. Нулевой байт не является законным в строке PostgreSQL. Точка не равна нулю. –

+0

@CraigRinger - не получилось бы получить буквальный '' \ u0000 "' в ответе строки? – Lix

0

Решение 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 
Смежные вопросы