Mise à jour : Avec PostgreSQL 9.5 , il existe des jsonb
fonctionnalités de manipulation dans PostgreSQL lui-même (mais aucune pour json
; les transtypages sont requis pour manipuler les json
valeurs).
Fusion de 2 (ou plusieurs) objets JSON (ou concaténation de tableaux):
SELECT jsonb '{"a":1}' || jsonb '{"b":2}', -- will yield jsonb '{"a":1,"b":2}'
jsonb '["a",1]' || jsonb '["b",2]' -- will yield jsonb '["a",1,"b",2]'
Ainsi, la définition d'une clé simple peut être effectuée en utilisant:
SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
Où <key>
doit être une chaîne et <value>
peut être n'importe quel type to_jsonb()
accepté.
Pour définir une valeur profondément dans une hiérarchie JSON , la jsonb_set()
fonction peut être utilisée:
SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
Liste complète des paramètres de jsonb_set()
:
jsonb_set(target jsonb,
path text[],
new_value jsonb,
create_missing boolean default true)
path
peut également contenir des index de tableaux JSON et des entiers négatifs qui apparaissent à partir de la fin des tableaux JSON. Cependant, un index de tableau JSON inexistant mais positif ajoutera l'élément à la fin du tableau:
SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
Pour l' insertion dans le tableau JSON (tout en préservant toutes les valeurs d'origine) , la jsonb_insert()
fonction peut être utilisée ( en 9.6+; cette fonction uniquement, dans cette section ):
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2')
-- will yield jsonb '{"a":[null,{"b":[2,1]}]}', and
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2', true)
-- will yield jsonb '{"a":[null,{"b":[1,2]}]}'
Liste complète des paramètres de jsonb_insert()
:
jsonb_insert(target jsonb,
path text[],
new_value jsonb,
insert_after boolean default false)
Encore une fois, des nombres entiers négatifs qui apparaissent en path
nombre à partir de la fin des tableaux JSON.
Donc, ex. l'ajout à la fin d'un tableau JSON peut se faire avec:
SELECT jsonb_insert('{"a":[null,{"b":[1,2]}]}', '{a,1,b,-1}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}', and
Cependant, cette fonction fonctionne un peu différemment (que jsonb_set()
) lorsque l' path
en target
est la clé d'un objet JSON. Dans ce cas, il n'ajoutera une nouvelle paire clé-valeur pour l'objet JSON que lorsque la clé n'est pas utilisée. S'il est utilisé, il générera une erreur:
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,c}', jsonb '[2]')
-- will yield jsonb '{"a":[null,{"b":[1],"c":[2]}]}', but
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b}', jsonb '[2]')
-- will raise SQLSTATE 22023 (invalid_parameter_value): cannot replace existing key
La suppression d'une clé (ou d'un index) d'un objet JSON (ou d'un tableau) peut se faire avec l' -
opérateur:
SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
jsonb '["a",1,"b",2]' - 1 -- will yield jsonb '["a","b",2]'
La suppression, au plus profond d'une hiérarchie JSON, peut se faire avec l' #-
opérateur:
SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Pour 9.4 , vous pouvez utiliser une version modifiée de la réponse d'origine (ci-dessous), mais au lieu d'agréger une chaîne JSON, vous pouvez l'agréger directement dans un objet json avec json_object_agg()
.
Réponse originale : Il est possible (sans plpython ou plv8) également en SQL pur (mais nécessite 9.3+, ne fonctionnera pas avec 9.2)
CREATE OR REPLACE FUNCTION "json_object_set_key"(
"json" json,
"key_to_set" TEXT,
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> "key_to_set"
UNION ALL
SELECT "key_to_set", to_json("value_to_set")) AS "fields"
$function$;
SQLFiddle
Modifier :
Une version, qui définit plusieurs clés et valeurs:
CREATE OR REPLACE FUNCTION "json_object_set_keys"(
"json" json,
"keys_to_set" TEXT[],
"values_to_set" anyarray
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> ALL ("keys_to_set")
UNION ALL
SELECT DISTINCT ON ("keys_to_set"["index"])
"keys_to_set"["index"],
CASE
WHEN "values_to_set"["index"] IS NULL THEN 'null'::json
ELSE to_json("values_to_set"["index"])
END
FROM generate_subscripts("keys_to_set", 1) AS "keys"("index")
JOIN generate_subscripts("values_to_set", 1) AS "values"("index")
USING ("index")) AS "fields"
$function$;
Edit 2 : comme @ErwinBrandstetter l'a noté, ces fonctions ci-dessus fonctionnent comme un soi-disant UPSERT
(met à jour un champ s'il existe, insère s'il n'existe pas). Voici une variante, qui seulement UPDATE
:
CREATE OR REPLACE FUNCTION "json_object_update_key"(
"json" json,
"key_to_set" TEXT,
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT CASE
WHEN ("json" -> "key_to_set") IS NULL THEN "json"
ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> "key_to_set"
UNION ALL
SELECT "key_to_set", to_json("value_to_set")) AS "fields")::json
END
$function$;
Edit 3 : Voici une variante récursive, qui peut définir ( UPSERT
) une valeur de feuille (et utilise la première fonction de cette réponse), située sur un chemin de clé (où les clés ne peuvent se référer qu'à des objets internes, des tableaux internes non pris en charge):
CREATE OR REPLACE FUNCTION "json_object_set_path"(
"json" json,
"key_path" TEXT[],
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT CASE COALESCE(array_length("key_path", 1), 0)
WHEN 0 THEN to_json("value_to_set")
WHEN 1 THEN "json_object_set_key"("json", "key_path"[l], "value_to_set")
ELSE "json_object_set_key"(
"json",
"key_path"[l],
"json_object_set_path"(
COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
"key_path"[l+1:u],
"value_to_set"
)
)
END
FROM array_lower("key_path", 1) l,
array_upper("key_path", 1) u
$function$;
Mise à jour : les fonctions sont maintenant compactées.
select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two');
message d'erreur estERROR: could not determine polymorphic type because input has type "unknown"