Postgresql 9.4 - 转换为 JSONB 时输入语法无效

Posted

技术标签:

【中文标题】Postgresql 9.4 - 转换为 JSONB 时输入语法无效【英文标题】:Postgresql 9.4 - Invalid input syntax when converting to JSONB 【发布时间】:2017-06-14 12:02:30 【问题描述】:

我制作了一个简单的函数来用新值更新 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() 【参考方案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($1) a
 LEFT JOIN jsonb_each($2) b USING (key)
$func$;

【讨论】:

我之前尝试过|| 运算符,但没有成功。然后我发现 PostgreSQL 9.4 不支持它。所以我创建了这个函数。感谢您的帮助! @PedroCorso 是的,它是在 9.5 中引入的。我有一个 detailed answer here 来说明你想要为每个 Postgres 版本回到 9.3 实现的目标。

以上是关于Postgresql 9.4 - 转换为 JSONB 时输入语法无效的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 9.4:在数组内的 JSON 字段 id 上聚合/连接表

如何使用 Postgresql 将行转换为列?

使用 postgres 9.4 将 JSON 元素附加到数组

使用 PostgreSQL 的数据透视视图

如何在 postgresql 中对 json 类型使用唯一约束

PostgreSQL 9.2 - 将 TEXT json 字符串转换为 json/hstore 类型