Обработка последовательностей Unicode в postgresql
У меня есть данные JSON, хранящиеся в столбце JSON (не JSONB) в моей базе данных postgresql (9.4.1). Некоторые из этих структур JSON содержат последовательности Unicode в их значениях атрибутов. Например:
{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }
Когда я пытаюсь запросить этот столбец JSON (даже если я не пытаюсь напрямую получить доступ к атрибуту device_name
), я получаю следующую ошибку:
ОШИБКА: неподдерживаемая escape-последовательность 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 как упомянутое в журнале изменений:
... Поэтому \u0000
теперь также будет отклоняться в значениях json, когда требуется преобразование в отмененную форму. Это изменение не нарушает возможности сохранения \u0000
в столбцах json, если обработка значений не выполняется...
Это действительно было намерение? Является ли понижение до 9.4.1 жизнеспособным вариантом здесь?
<суб > В качестве дополнительной заметки это свойство берется из имени клиентского мобильного устройства - пользователя, который ввел этот текст в устройство. Как на самом деле пользователь ввел NULL
и REPLACEMENT CHARACTER
значения?!
Суб >
Ответы
Ответ 1
\u0000
- это одна точка кода Unicode, которая недопустима в строке. Я не вижу другого способа, кроме как очистить строку.
Так как json
- это просто строка в определенном формате, вы можете использовать стандартные строковые функции, не беспокоясь о структуре JSON. Однострочным дезинфицирующим средством для удаления кодовой точки будет:
SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;
Но вы также можете вставить любой символ по своему вкусу, который был бы полезен, если нулевая кодовая точка используется как некоторая форма разделителя.
Отметьте также тонкую разницу между тем, что хранится в базе данных и как она представлена пользователю. Вы можете сохранить точку кода в строке JSON, но перед обработкой значения в качестве типа данных 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