Postgresql 9.4 - 转换为 JSONB 时输入语法无效
Postgresql 9.4 - Invalid input syntax when converting to JSONB
我做了一个简单的函数来用新值更新 jsonb:
CREATE OR REPLACE FUNCTION jsonupdate(
IN "pJson" jsonb, IN "pNewValues" jsonb)
RETURNS jsonb AS
$BODY$
DECLARE
jsonreturn jsonb;
BEGIN
jsonreturn := (SELECT json_object_agg(keyval.key, keyval.value::jsonb)
FROM (SELECT key,
CASE WHEN "pNewValues" ? key THEN
(SELECT "pNewValues" ->> key)
ELSE
value
END
FROM jsonb_each_text("pJson")) keyval);
RETURN jsonreturn;
END; $BODY$
LANGUAGE plpgsql IMMUTABLE
COST 100;
示例输入和输出:
输入:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": "3"}');
输出:{"a": 3, "b": 2}
输入:SELECT jsonupdate('{"a" : "3", "b" : { "c": "text", "d": 1 }}', '{"b": { "c": "another text" }}');
输出:{"a": 3, "b": {"c": "another text"}}
输入:SELECT jsonupdate('{"a" : "1", "b" : "2", "c": 3, "d": 4}', '{"a": "5", "d": 6}');
输出:{"a": 5, "b": 2, "c": 3, "d": 6}
当使用像这样的输入时会发生问题:SELECT jsonupdate('{"a" : "1", "b" : ""}', '{"a": "5"}')
或这个:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": "."}')
或这个:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": ""}')
它给我一个错误
ERROR: invalid input syntax for type json
DETAIL: The input string ended unexpectedly.
CONTEXT: JSON data, line 1:
这是怎么回事?
您应该使用 jsonb_each()
函数(而不是 jsonb_each_text()
)。此外,->
运算符(而不是 ->>
):
CREATE OR REPLACE FUNCTION jsonupdate(IN "pJson" jsonb, IN "pNewValues" jsonb)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE AS
$BODY$
SELECT json_object_agg(key, CASE
WHEN "pNewValues" ? key THEN "pNewValues" -> key
ELSE value
END)
FROM jsonb_each("pJson")
$BODY$;
jsonb_each_text()
和 ->>
运算符将任何非字符串 JSON 值转换为其字符串表示形式。将它们转换回 JSON 将以您可能不希望的方式修改您的数据。
但我不得不承认,您要实现的几乎是 ||
(串联)运算符。即
SELECT jsonb '{"a" : "1", "b" : "2"}' || jsonb '{"a": "3"}'
将为您提供所需的输出。 ||
和您的函数之间的唯一区别是当 pNewValues
包含不在 pJson
中的键时:||
也会追加这些键,而您的函数不会附加它们(它只修改现有的)。
更新:模拟9.4上的||
操作符,可以使用如下函数:
CREATE OR REPLACE FUNCTION jsonb_merge_objects(jsonb, jsonb)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE AS
$func$
SELECT json_object_agg(key, COALESCE(b.value, a.value))
FROM jsonb_each() a
LEFT JOIN jsonb_each() b USING (key)
$func$;
我做了一个简单的函数来用新值更新 jsonb:
CREATE OR REPLACE FUNCTION jsonupdate(
IN "pJson" jsonb, IN "pNewValues" jsonb)
RETURNS jsonb AS
$BODY$
DECLARE
jsonreturn jsonb;
BEGIN
jsonreturn := (SELECT json_object_agg(keyval.key, keyval.value::jsonb)
FROM (SELECT key,
CASE WHEN "pNewValues" ? key THEN
(SELECT "pNewValues" ->> key)
ELSE
value
END
FROM jsonb_each_text("pJson")) keyval);
RETURN jsonreturn;
END; $BODY$
LANGUAGE plpgsql IMMUTABLE
COST 100;
示例输入和输出:
输入:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": "3"}');
输出:{"a": 3, "b": 2}
输入:SELECT jsonupdate('{"a" : "3", "b" : { "c": "text", "d": 1 }}', '{"b": { "c": "another text" }}');
输出:{"a": 3, "b": {"c": "another text"}}
输入:SELECT jsonupdate('{"a" : "1", "b" : "2", "c": 3, "d": 4}', '{"a": "5", "d": 6}');
输出:{"a": 5, "b": 2, "c": 3, "d": 6}
当使用像这样的输入时会发生问题:SELECT jsonupdate('{"a" : "1", "b" : ""}', '{"a": "5"}')
或这个:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": "."}')
或这个:SELECT jsonupdate('{"a" : "1", "b" : "2"}', '{"a": ""}')
它给我一个错误
ERROR: invalid input syntax for type json DETAIL: The input string ended unexpectedly. CONTEXT: JSON data, line 1:
这是怎么回事?
您应该使用 jsonb_each()
函数(而不是 jsonb_each_text()
)。此外,->
运算符(而不是 ->>
):
CREATE OR REPLACE FUNCTION jsonupdate(IN "pJson" jsonb, IN "pNewValues" jsonb)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE AS
$BODY$
SELECT json_object_agg(key, CASE
WHEN "pNewValues" ? key THEN "pNewValues" -> key
ELSE value
END)
FROM jsonb_each("pJson")
$BODY$;
jsonb_each_text()
和 ->>
运算符将任何非字符串 JSON 值转换为其字符串表示形式。将它们转换回 JSON 将以您可能不希望的方式修改您的数据。
但我不得不承认,您要实现的几乎是 ||
(串联)运算符。即
SELECT jsonb '{"a" : "1", "b" : "2"}' || jsonb '{"a": "3"}'
将为您提供所需的输出。 ||
和您的函数之间的唯一区别是当 pNewValues
包含不在 pJson
中的键时:||
也会追加这些键,而您的函数不会附加它们(它只修改现有的)。
更新:模拟9.4上的||
操作符,可以使用如下函数:
CREATE OR REPLACE FUNCTION jsonb_merge_objects(jsonb, jsonb)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE AS
$func$
SELECT json_object_agg(key, COALESCE(b.value, a.value))
FROM jsonb_each() a
LEFT JOIN jsonb_each() b USING (key)
$func$;