web-dev-qa-db-fra.com

"ERREUR: littéral de tableau mal formé" lors de l'utilisation de json_to_record avec un élément de tableau JSON dans Postgres 9.4

Cela illustre bien le problème:

Lorsque la colonne b est de type texte et non un tableau, les opérations suivantes fonctionnent:

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

Mais si je définis la colonne b comme un tableau, j'obtiens cette erreur:

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.

Comment puis-je convaincre/contraindre json_to_record (ou json_populate_record) pour convertir un tableau JSON en tableau Postgres du type de colonne cible?

9
Taytay

Juste une légère variation de la réponse 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);

L'idée est la même: masser le tableau JSON dans un tableau - dans ce cas, à travers un tableau littéral. En plus d'un code un peu plus propre (bien que je l'aime, l'expression régulière n'aide pas beaucoup à cet égard :), elle semble légèrement plus rapide aussi:

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

Sur cet ensemble de données et sur ma boîte de test, la version regex affiche et le temps d'exécution moyen de 0 ms, tandis que ma version affiche 210 ms.

6
dezso

Ce n'est peut-être pas la solution la plus élégante, mais cela résoudra vos problèmes ...

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

Son fonctionnement est assez simple:

First, prenez la chaîne text dans b, et supprimez-la aux informations utiles. Cela se fait en utilisant regexp_replace() as

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

pour supprimer toutes les instances de [, ", ] et tous les espaces, ou plus précisément, pour remplacer toute instance de ces caractères par '' , et pour l'appliquer globalement, signalé par l'utilisation de l'indicateur 'g'.

Next, divisez simplement la chaîne en un tableau en utilisant string_to_array() as

string_to_array(your_string,',')

où dans ce cas your_string est simplement le résultat de la regexp_replace() ci-dessus. Le deuxième argument ',' A indiqué à string_to_array() que les éléments sont séparés par des virgules.

Cela produira un champ text[] Contenant les entrées souhaitées.

1
Chris