Как заставить PostgreSQL экранировать текст из jsonb_array_element?

#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"
}
  

db-скрипка

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"}