"ERROR: literal de matriz mal formado" cuando se usa json_to_record con un elemento de matriz JSON en Postgres 9.4

9

Esto ilustra muy bien el problema:

Cuando la columna b es de tipo texto, y no una matriz, funciona lo siguiente:

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

Pero si defino la bcolumna como una matriz, me sale este error:

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.

¿Cómo puedo convencer / coaccionar json_to_record(o json_populate_record) para convertir una matriz JSON en la matriz Postgres del tipo de columna de destino?

Taytay
fuente

Respuestas:

6

Solo una ligera variación a la respuesta de Chris:

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);

La idea es la misma: masajear la matriz JSON en una matriz, en este caso, a través de una matriz literal. Además de un código un poco más limpio (aunque me encanta, la expresión regular generalmente no ayuda mucho en este sentido :), también parece un poco más rápido:

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);

En este conjunto de datos y en mi cuadro de prueba, la versión regex muestra un tiempo de ejecución promedio de 300 ms , mientras que mi versión muestra 210 ms .

dezso
fuente
1

Puede que esta no sea la solución más elegante, pero solucionará sus problemas ...

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);

Es bastante sencillo cómo funciona:

Primero , tome la textcadena by destrúyala hasta la información útil. Esto se hace usando regexp_replace()como

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

para eliminar todas las instancias de [, ", ], y cualquier espacio en blanco, o más específicamente, para reemplazar todas las instancias de estos personajes con '', y se aplican a este mundo, señalado por el uso de la bandera 'g'.

A continuación , simplemente divida la cadena en una matriz usando string_to_array()como

string_to_array(your_string,',')

donde en este caso your_stringes simplemente el resultado de lo anterior regexp_replace(). El segundo argumento ','indica string_to_array()que los elementos están separados por comas.

Esto generará un text[]campo que contiene las entradas deseadas.

Chris
fuente