将json转换为嵌套的postgres复合类型

时间:2016-10-14 16:24:50

标签: json postgresql compositetype

我在postgres中定义了以下嵌套类型:

CREATE TYPE address AS (
  name    text,
  street  text,
  zip     text,
  city    text,
  country text
);

CREATE TYPE customer AS (
  customer_number           text,
  created                   timestamp WITH TIME ZONE,
  default_billing_address   address,
  default_shipping_address  address
);

现在想在存储过程中填充这些类型,它将json作为输入参数。这适用于顶级字段,输出显示postgres复合类型的内部格式:

# select json_populate_record(null::customer, '{"customer_number":"12345678"}'::json)::customer;
 json_populate_record 
----------------------
 (12345678,,,)
(1 row)

但是,postgres不处理嵌套的json结构:

# select json_populate_record(null::customer, '{"customer_number":"12345678","default_shipping_address":{"name":"","street":"","zip":"12345","city":"Berlin","country":"DE"}}'::json)::customer;
ERROR:  malformed record literal: "{"name":"","street":"","zip":"12345","city":"Berlin","country":"DE"}"
DETAIL:  Missing left parenthesis.

如果嵌套属性在postgres中,那又是有效的。内部格式如下:

# select json_populate_record(null::customer, '{"customer_number":"12345678","default_shipping_address":"(\"\",\"\",12345,Berlin,DE)"}'::json)::customer;
            json_populate_record            
--------------------------------------------
 (12345678,,,"("""","""",12345,Berlin,DE)")
(1 row)

有没有办法让postgres从嵌套的json结构转换为相应的复合类型?

3 个答案:

答案 0 :(得分:3)

仅对嵌套对象使用json_populate_record()

with a_table(jdata) as (
values
    ('{
        "customer_number":"12345678",
        "default_shipping_address":{
            "name":"",
            "street":"",
            "zip":"12345",
            "city":"Berlin",
            "country":"DE"
        }
    }'::json)
)
select (
    jdata->>'customer_number', 
    jdata->>'created', 
    json_populate_record(null::address, jdata->'default_billing_address'),
    json_populate_record(null::address, jdata->'default_shipping_address')
    )::customer
from a_table;

                    row                     
--------------------------------------------
 (12345678,,,"("""","""",12345,Berlin,DE)")
(1 row) 

嵌套复合类型不是Postgres(和任何RDBMS)的设计目标。它们太复杂而且麻烦。 在数据库逻辑中,嵌套结构应保持为相关表,例如

create table addresses (
    address_id serial primary key,
    name text,
    street text,
    zip text,
    city text,
    country text
);

create table customers (
    customer_id serial primary key, -- not necessary `serial` may be `integer` or `bigint`
    customer_number text,           -- maybe redundant
    created timestamp with time zone,
    default_billing_address int references adresses(address_id),
    default_shipping_address int references adresses(address_id)
);

有时在表格中使用嵌套结构是合理的,但在这些情况下使用jsonbhstore似乎更方便和自然,例如:

create table customers (
    customer_id serial primary key, 
    customer_number text,
    created timestamp with time zone,
    default_billing_address jsonb,
    default_shipping_address jsonb
);

答案 1 :(得分:2)

plpython救援:

create function to_customer (object json)
returns customer
AS $$
import json
return json.loads(object)
$$ language plpythonu;

示例:

select to_customer('{
        "customer_number":"12345678",
        "default_shipping_address":
        {
                "name":"",
                "street":"",
                "zip":"12345",
                "city":"Berlin",
                "country":"DE"
        },
        "default_billing_address":null,
        "created": null
}'::json);
                to_customer                 
--------------------------------------------
 (12345678,,,"("""","""",12345,Berlin,DE)")
(1 row)

警告:postgresql从python构建返回的对象时需要将所有null值作为None存在(即不允许跳过空值而不存在),因此我们必须指定所有传入json中的空值。例如,不允许:

select to_customer('{
        "customer_number":"12345678",
        "default_shipping_address":
        {
                "name":"",
                "street":"",
                "zip":"12345",
                "city":"Berlin",
                "country":"DE"
        } 
}'::json);                             
ERROR:  key "created" not found in mapping
HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
CONTEXT:  while creating return value
PL/Python function "to_customer"

答案 2 :(得分:0)

这似乎在Postgres 10中得到解决。在json_populate_record中搜索var needle = 234; var haystack = 79234826; var contains = haystack.ToString().Contains(needle.ToString()); 会显示以下更改:

  

让json_populate_record()和相关函数递归处理JSON数组和对象(Nikita Glukhov)

     

通过此更改,目标SQL类型中的数组类型字段可以从JSON数组正确转换,并且可以从JSON对象正确转换复合类型字段。以前,这种情况会失败,因为JSON值的文本表示将被提供给array_in()或record_in(),并且其语法与这些输入函数所期望的不匹配。