Atualizar :Com o PostgreSQL 9.5, existem alguns
jsonb
funcionalidade de manipulação dentro do próprio PostgreSQL (mas nenhuma para json
; casts são necessários para manipular json
valores). Mesclando 2 (ou mais) objetos JSON (ou concatenando arrays):
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]'
Então, definindo uma chave simples pode ser feito usando:
SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
Onde
<key>
deve ser string e <value>
pode ser qualquer tipo to_jsonb()
aceita. Para definir um valor profundo em uma hierarquia JSON , o
jsonb_set()
função pode ser usada:SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
Lista completa de parâmetros de
jsonb_set()
:jsonb_set(target jsonb,
path text[],
new_value jsonb,
create_missing boolean default true)
path
também pode conter índices de matriz JSON e inteiros negativos que aparecem lá contam a partir do final de matrizes JSON. No entanto, um índice de matriz JSON inexistente, mas positivo, anexará o elemento ao final da matriz:SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
Para inserir na matriz JSON (enquanto preserva todos os valores originais) , o
jsonb_insert()
pode ser usada (em 9.6+; esta função apenas, nesta seção ):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]}]}'
Lista completa de parâmetros de
jsonb_insert()
:jsonb_insert(target jsonb,
path text[],
new_value jsonb,
insert_after boolean default false)
Novamente, inteiros negativos que aparecem em
path
contagem a partir do final das matrizes JSON. Então, f.ex. anexar a um final de uma matriz JSON pode ser feito com:
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
No entanto, esta função está funcionando um pouco diferente (do que
jsonb_set()
) quando o path
em target
é a chave de um objeto JSON. Nesse caso, ele só adicionará um novo par chave-valor para o objeto JSON quando a chave não for usada. Se for usado, ele irá gerar um erro: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
Excluindo uma chave (ou um índice) de um objeto JSON (ou, de um array) pode ser feito com o
-
operador:SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
jsonb '["a",1,"b",2]' - 1 -- will yield jsonb '["a","b",2]'
Exclusão, do fundo de uma hierarquia JSON pode ser feito com o
#-
operador:SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Para 9.4 , você pode usar uma versão modificada da resposta original (abaixo), mas em vez de agregar uma string JSON, você pode agregar em um objeto json diretamente com
json_object_agg()
. Resposta original :É possível (sem plpython ou plv8) em SQL puro também (mas precisa de 9.3+, não funcionará com 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
Editar :
Uma versão, que define várias chaves e valores:
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$;
Editar 2 :como @ErwinBrandstetter observou, essas funções acima funcionam como um chamado
UPSERT
(atualiza um campo se existir, insere se não existir). Aqui está uma variante, que apenas 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$;
Editar 3 :Aqui está a variante recursiva, que pode definir (
UPSERT
) um valor de folha (e usa a primeira função desta resposta), localizado em um caminho de chave (onde as chaves podem se referir apenas a objetos internos, matrizes internas não suportadas):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$;
Atualizado:Adicionada função para substituir a chave de um campo json existente por outra chave fornecida. Pode ser útil para atualizar tipos de dados em migrações ou outros cenários, como alteração de estrutura de dados.
CREATE OR REPLACE FUNCTION json_object_replace_key(
json_value json,
existing_key text,
desired_key text)
RETURNS json AS
$BODY$
SELECT COALESCE(
(
SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}')
FROM (
SELECT *
FROM json_each(json_value)
WHERE key <> existing_key
UNION ALL
SELECT desired_key, json_value -> existing_key
) AS "fields"
-- WHERE value IS NOT NULL (Actually not required as the string_agg with value's being null will "discard" that entry)
),
'{}'
)::json
$BODY$
LANGUAGE sql IMMUTABLE STRICT
COST 100;
Atualizar :as funções estão compactadas agora.