#json #postgresql
#json #postgresql
Вопрос:
Я загружаю некоторый JSON из Postgres 13 в Elasticsearch с помощью Logstash и столкнулся с некоторыми ошибками, вызванными тем, что текст не экранируется с помощью обратного solidus. Я отследил свою проблему до такого поведения:
SELECT
json_build_object(
'literal_text', 'abcndef'::text,
'literal_text_type', pg_typeof('abcndef'::text),
'text_from_jsonb_array_element', a->>0,
'jsonb_array_element_type', pg_typeof(a->>0)
)
FROM jsonb_array_elements('["abcndef"]') jae (a);
{
"literal_text": "abc\ndef",
"literal_text_type": "text",
"text_from_jsonb_array_element": "abcndef",
"jsonb_array_element_type":"text"
}
json_build_object
кодирует буквальный текст, как и ожидалось (превращаясь n
в \n
); однако он не кодирует текст, полученный с помощью jsonb_array_element
, хотя оба являются text
.
Почему text
извлеченный из jsonb_array_element
обрабатывается по-разному (не экранируется jsonb_build_object
)? Я пробовал кастинг, используя jsonb_array_elements_text
(хотя мой фактический вариант использования включает массив массивов, поэтому мне нужно разделить на набор jsonb
) и различные функции экранирования / кодирования / форматирования, но пока не нашел решения.
Есть ли трюк для приведения текста, извлеченного из jsonb_array_element
, чтобы он был правильно закодирован jsonb_build_object
?
Спасибо за любые подсказки или решения.
Ответ №1:
Эти строки выглядят ужасно похожими, но на самом деле они разные. Когда вы создаете строковый литерал like 'n'
, это символ обратной косой черты, за которым следует символ «n». Поэтому, когда вы вводите это json_build_object
, ему нужно добавить обратную косую черту, чтобы избежать обратной косой черты, которую вы ему даете.
С другой стороны, когда вы вызываете jsonb_array_elements('["abcndef"]')
, вы говорите, что в JSON точно n
закодирован a без второй обратной косой черты, и поэтому, когда он преобразуется в текст, он n
интерпретируется как символ новой строки, а не как два отдельных символа. Вы можете легко убедиться в этом, выполнив следующее:
SELECT a->>0 FROM jsonb_array_elements('["abcndef"]') a;
?column?
----------
abc
def
(1 row)
При кодировании этого обратно в JSON вы снова получаете одну обратную косую черту, потому что она снова кодирует символ новой строки.
Если вы хотите экранировать его с помощью дополнительной обратной косой черты, я предлагаю простую замену:
SELECT
json_build_object(
'text_from_jsonb_with_replace', replace(a->>0, E'n', 'n')
)
FROM jsonb_array_elements('["abcndef"]') jae (a);
json_build_object
------------------------------------------------
{"text_from_jsonb_with_replace" : "abc\ndef"}