In my solution I presume that objects in your JSON array have the same structure. So we create an SQL type
for this objects:
DROP TYPE IF EXISTS item_type;
CREATE TYPE item_type
AS ("ref" int, "desc" VARCHAR(500), price FLOAT, quant INTEGER);
--ref and desc are SQL key words
--so you need to specify that they actually are column names
The demo table looks like this:
CREATE TABLE items
(
id SERIAL NOT NULL
CONSTRAINT items_pkey
PRIMARY KEY,
content jsonb DEFAULT '[]'::jsonb NOT NULL
);
The query:
SELECT xmlelement(name items, (--create the root element "items"
SELECT xmlagg(--aggregate "item" elements
xmlelement(NAME item, --create "item" elements
xmlforest(t."ref", t."desc", t.price, t.quant)--create "item" element content
)
)
FROM
(
SELECT (jsonb_populate_recordset(NULL :: item_type, items.content)).*--create records ot of JSON array
FROM items
WHERE items.id = 1 --you can remove where if you want all rows
) AS t
))
Explanation:
From inside out; (1) create SQL records of the type we created earlier
(the item_type
) out of the JSON array using jsonb_populate_recordset
, (2) create the content of the <item>
nodes using xmlforest
, (3) create the <item>
element itself using xmlelement
, (4) aggregate <item>
elements using xmlagg
, create the root element <items>
using xmlelement
.
SQLFiddle - it runs but it looks like SQLFiddle have problems outputting XML results so I cast the result to TEXT
.