«ОШИБКА: некорректный литерал массива» при использовании json_to_record с элементом массива JSON в Postgres 9.4

9

Это хорошо иллюстрирует проблему:

Когда столбец b имеет тип text, а не массив, работает следующее:

select * 
from json_to_record('{"a":1,"b":["hello", "There"],"c":"bar"}') 
    as x(a int, b text, d text);

 a |         b          | d
---+--------------------+---
 1 | ["hello", "There"] |

Но если я определю bстолбец как массив, я получу эту ошибку:

select * 
from json_to_record('{"a":1,"b":["hello", "There"],"c":"bar"}') 
    as x(a int, b text[], d text)

ERROR:  malformed array literal: "["hello", "There"]"
DETAIL:  "[" must introduce explicitly-specified array dimensions.

Как я могу убедить / принудительно json_to_record(или json_populate_record) преобразовать массив JSON в массив Postgres целевого типа столбца?

Тайтай
источник

Ответы:

6

Небольшое отклонение от ответа Криса:

SELECT a, translate(b, '[]', '{}')::text[] AS b, d
FROM json_to_record('{"a": 1, "b": ["hello", "There"], "c": "bar"}')
AS x(a int, b text, d text);

Идея та же: втирать массив JSON в массив - в данном случае через литерал массива. В дополнение к немного более чистому коду (хотя мне это нравится, регулярные выражения обычно не сильно помогают в этом отношении :), он также кажется немного быстрее:

CREATE TABLE jsonb_test (
    id serial,
    data jsonb
);

INSERT INTO jsonb_test (id, data)
SELECT i, format('{"a": %s, "b": ["foo", "bar"], "c": "baz"}', i::text)::jsonb 
FROM generate_series(1,10000) t(i);

SELECT a, string_to_array(regexp_replace(b, '\[*\"*\s*\]*','','g'),',') AS b, d
FROM jsonb_test AS j, 
LATERAL json_to_record(j.data::json) AS r(a int, b text, d text);

-- versus 

SELECT a, translate(b, '[]', '{}')::text[] AS b, d
FROM jsonb_test AS j, 
LATERAL json_to_record(j.data::json) AS r(a int, b text, d text);

В этом наборе данных и в моем тестовом окне версия регулярного выражения показывает среднее время выполнения 300 мс , а моя версия - 210 мс .

Dezso
источник
1

Возможно, это не самое элегантное решение, но оно решит ваши проблемы ...

SELECT a,string_to_array(regexp_replace(b, '\[*\"*\s*\]*','','g'),',') AS b,d
FROM json_to_record('{"a":1,"b":["hello", "There"],"c":"bar"}')
AS x(a int, b text, d text);

Это довольно просто, как это работает:

Сначала возьмите textстроку bи разденьте ее до полезной информации. Это делается с помощью regexp_replace()как

regexp_replace(b, '\[*\"*\s*\]*','','g')

чтобы удалить все экземпляры [, ", ], и любые пробельные символы, или , более конкретно, заменить все экземпляры этих символов с '', и применять это в глобальном масштабе, сигнализируется с помощью флага 'g'.

Далее , просто разбить строку на массив , используя в string_to_array()качестве

string_to_array(your_string,',')

где в данном случае your_stringэто просто результат вышеописанного regexp_replace(). Второй аргумент ','указывает на string_to_array()то, что элементы разделены запятыми.

Это даст text[]поле, содержащее ваши нужные записи.

Крис
источник