2016-10-14 20 views
5

mam następujące typy zagnieżdżone zdefiniowane w PostgreSQL:Konwersja json do zagnieżdżony typu postgres kompozytowego

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 
); 

I będzie teraz chciał zapełnić to typy w procedurze przechowywanej, który dostaje json jako parametr wejściowy. Działa to dla pól na najwyższym poziomie, wyjście pokazuje mi wewnętrzny format typu postgres kompozytowe:

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

Jednak postgres nie obsługuje zagnieżdżone struktury 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. 

Co działa znowu jest, jeżeli nieruchomość jest zagnieżdżona w formacie wewnętrznym postgres' jak tutaj:

# 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) 

czy istnieje jakiś sposób, aby uzyskać postgres przekonwertować z zagnieżdżonej struktury json do odpowiedniego typu kompozytowego ?

Odpowiedz

1

plpython na ratunek:

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

przykład:

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) 

Ostrzeżenie: Postgresql podczas budowania zwracanego obiektu z Pythona wymaga obecności wszystkich wartości null jako None (tj. nie można pominąć wartości null jako nieobecnych), dlatego musimy określić wszystkie wartości puste w przychodzącym jsonie. Na przykład, nie wolno:

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" 
+0

Podczas gdy prawdopodobnie nie chcę polegać na plpython dla tego problemu, ta odpowiedź pokazuje, że istnieje ogólne rozwiązanie i że postgres miałby wszystkie informacje potrzebne do jego wdrożenia. –

3

Stosować json_populate_record() tylko dla obiektów zagnieżdżonych:

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) 

zagnieżdżone typy kompozytowe nie są co Postgres (i wszelkich RDBMS) został zaprojektowany dla. Są zbyt skomplikowane i kłopotliwe. W strukturach zagnieżdżonych w logice bazy danych należy zachować podobne tabele, np.

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) 
); 

Czasami jest to rozsądne, aby mieć zagnieżdżone struktury w tabeli, ale wydaje się bardziej wygodne i naturalne używać jsonb lub hstore w tych przypadkach, np:

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

To działałoby, ale w przypadku większych lub bardziej zagnieżdżonych obiektów chciałbym uniknąć powtarzania wszystkich kolumn i ich kolejności w typie złożonym. –

+0

Obawiam się, że nie masz wyboru. – klin

+0

Aby podać nieco więcej kontekstu na temat użycia typów zagnieżdżonych, byłyby to wartości zwracane procedury przechowywanej lub zapytania, tabele bazowe byłyby dobrze znormalizowane. Zalando robi coś podobnego po stronie klienta w pakiecie java-sproc-wrapper (https://github.com/zaland-incubator/java-sproc-wrapper/#type-mapping), ale najpierw musi wyodrębnić cały typ informacje z bazy danych. Poprzez mapowanie typów do iz json, implementacja może być znacznie prostsza. –

0

To wydaje się być rozwiązany w PostgreSQL 10. Przeszukiwanie release notes dla json_populate_record pokazuje następujące zmiany:

Producent json_populate_record() oraz funkcje związane z tablic i obiektów JSON procesowe rekursywnie (Nikita Glukhov)

Po tej zmianie pola typu tablicy w docelowym typie SQL są poprawnie konwertowane z tablic JSON, a pola typu złożonego są poprawnie konwertowane z obiektów JSON. Poprzednio takie przypadki kończyły się niepowodzeniem, ponieważ tekstowa reprezentacja wartości JSON byłaby przekazywana do array_in() lub record_in(), a jej składnia nie odpowiadałaby oczekiwanym funkcjom wejściowym.