xmlelement
PG 8.3+→ xmlProduces an XML element with the given name, optional attributes, and optional content.
Signature
xmlelement ( NAME name [, XMLATTRIBUTES ( value [ AS attname ] [, ...] )] [, content [, ...]] ) → xmlParameters
| Parameter | Type | Description |
|---|---|---|
| name | identifier | XML element name |
| attributes | expressions via XMLATTRIBUTES | Optional XML attributes |
| content | any | Element content (text or nested XML) |
Examples
SELECT xmlelement(NAME foo);<foo/>SELECT xmlelement(NAME greeting, 'hello world');<greeting>hello world</greeting>SELECT xmlelement(NAME user, XMLATTRIBUTES(id AS id, name AS name)) FROM users;<user id="1" name="Alice"/>SELECT xmlelement(NAME item, XMLATTRIBUTES(sku AS sku), xmlelement(NAME price, price), xmlelement(NAME stock, qty)) FROM products;<item sku="ABC"><price>9.99</price><stock>100</stock></item>SELECT xmlelement(NAME result, XMLATTRIBUTES(NOW() AS generated_at), xmlagg(xmlelement(NAME row, id))) FROM logs;<result generated_at="2024-01-01T00:00:00"><row>1</row>...</result>Concatenating strings to form XML (e.g., `'<tag>' || value || '</tag>'`) bypasses proper escaping, producing broken XML when values contain `<`, `>`, `&`, or quotes. `xmlelement` escapes content automatically.
✓ Instead: Always use `xmlelement(NAME tag, value)` — PostgreSQL handles character escaping correctly, so `&` becomes `&` and `<` becomes `<` in text nodes.
Use `xmlelement` for the outer wrapper and `xmlforest` for a set of child elements from row columns. This is cleaner than nesting multiple `xmlelement` calls.
SELECT xmlelement(NAME order, XMLATTRIBUTES(id), xmlforest(customer_id AS customer, total AS amount, status)) FROM orders LIMIT 3;<order id="1"><customer>42</customer><amount>100.00</amount><status>paid</status></order>