#Constructors

XQuery doesn't just query XML — it builds it. Constructors let you create new elements, attributes, documents, and other nodes from scratch or from query results. This is where XQuery goes beyond XPath.

#Contents


#Direct Constructors

Direct constructors look like XML literals with embedded expressions in curly braces {}:

#Elements

<product id="NEW-001">
  <name>New Widget</name>
  <price>19.99</price>
</product>

This creates an XML element literally — no query needed.

#Nested Elements and Dynamic Content

Direct constructors can contain static and dynamic content freely mixed, including nested elements with embedded expressions:

<root>
  <item name="test">{current-dateTime()}</item>
  <nested><child>text</child></nested>
</root>

This produces exactly the XML you see, with {current-dateTime()} replaced by the current timestamp. The power of direct constructors is that the query output mirrors the structure of the XML you are building.

#Embedded Expressions

Curly braces {} embed XPath/XQuery expressions:

<product id="{ $id }">
  <name>{ $name }</name>
  <price>{ $price * (1 - $discount) }</price>
  <generated>{ current-dateTime() }</generated>
</product>

C# parallel: This is like XML literals in VB.NET, or building XElement in C#:

new XElement("product",
    new XAttribute("id", id),
    new XElement("name", name),
    new XElement("price", price * (1 - discount)),
    new XElement("generated", DateTime.Now)
)

The XQuery version is more readable because the output shape matches the XML structure.

#Embedding Query Results

Expressions can return sequences of nodes:

<catalog>{
  for $p in doc("products.xml")//product
  where $p/price < 50
  order by $p/name
  return <item>{ $p/name/text() } — ${ $p/price/text() }</item>
}</catalog>

Output:

<catalog>
  <item>USB-C Hub — $45.00</item>
  <item>Wireless Mouse — $29.99</item>
</catalog>

#Attribute Value Templates

Attributes use {} for dynamic values:

<a href="/products/{ $product/@id }.html"
   class="product { if ($product/@featured) then 'featured' else '' }">
  { $product/name/text() }
</a>

#Mixed Content

Text and elements can be freely mixed:

<p>
  Found { count(//error) } errors in
  <strong>{ $filename }</strong>
  processed at { current-time() }.
</p>

#Computed Constructors

When the element or attribute name is dynamic, use computed constructors:

#element

element { "h" || $level } { $heading-text }
(: Creates <h1>..., <h2>..., etc. based on $level :)
element { $tag-name } {
  attribute { "class" } { "dynamic" },
  text { "Hello" }
}

#attribute

element div {
  attribute class { "container" },
  attribute id { concat("section-", $num) },
  $content
}

#text, comment, processing-instruction

text { "plain text content" }
comment { "This is a generated comment" }
processing-instruction xml-stylesheet { 'type="text/css" href="style.css"' }

#When to Use Computed vs Direct

Scenario

Use

Known element name

Direct: <product>...</product>

Dynamic element name

Computed: element { $name } { ... }

Dynamic attribute name

Computed: attribute { $name } { $value }

Most cases

Direct — more readable


#Document Constructors

Create a complete document node:

document {
  processing-instruction xml-stylesheet { 'type="text/xsl" href="style.xsl"' },
  <root>
    <item>Content</item>
  </root>
}

#Namespaces in Constructors

#Declaring Namespaces

In direct constructors, use standard XML namespace declarations:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head><title>{ $title }</title></head>
  <body>{ $content }</body>
</html>

In the XQuery prolog:

declare namespace atom = "http://www.w3.org/2005/Atom";
<atom:feed>
  <atom:title>{ $feed-title }</atom:title>
  <atom:entry>
    <atom:title>{ $entry-title }</atom:title>
  </atom:entry>
</atom:feed>

#Computed Namespaces

element { QName("http://www.w3.org/2005/Atom", "feed") } {
  namespace atom { "http://www.w3.org/2005/Atom" },
  element { QName("http://www.w3.org/2005/Atom", "title") } { $title }
}

#String Constructors

XQuery 3.1 introduces string constructors using backtick syntax. These are useful when building strings that contain characters that would otherwise need escaping (curly braces, quotes, etc.):

(: Basic string constructor :)
let $name := "World"
return ``[Hello, `{$name}`! This is a {literal brace}.]``
(: Result: "Hello, World! This is a {literal brace}." :)

Inside a string constructor (delimited by [ and ] ), only expressions wrapped in `{ and }` are evaluated. Everything else is literal text, including curly braces.

(: Generating JSON-like strings :)
let $id := 42
let $label := "Widget"
return ``[{"id": `{$id}`, "label": "`{$label}`"}]``
(: Result: '{"id": 42, "label": "Widget"}' :)

String constructors are particularly useful for generating code, templates, or any output where the text itself contains XQuery's special characters.


#Common Patterns

#XML to XML Transformation

Convert between XML formats — similar to XSLT but in a query style:

(: Convert a product catalog to an RSS feed :)
<rss version="2.0">
  <channel>
    <title>Product Updates</title>
    <link>https://example.com</link>
    {
      for $product in //product
      return <item>
        <title>{ $product/name/text() }</title>
        <description>{ $product/description/text() }</description>
        <guid>{ string($product/@id) }</guid>
      </item>
    }
  </channel>
</rss>

#XML to JSON-ready Structures

Build maps and arrays for JSON output:

array {
  for $p in //product
  return map {
    "id": string($p/@id),
    "name": string($p/name),
    "price": number($p/price),
    "tags": array { $p/tags/tag ! string(.) }
  }
}

Serialize with serialize($result, map { "method": "json", "indent": true() }).

#Aggregation with Construction

Build summary documents:

<report generated="{ current-dateTime() }">
  <summary>
    <total-products>{ count(//product) }</total-products>
    <average-price>{ format-number(avg(//product/price), '#.00') }</average-price>
    <categories>{ count(distinct-values(//product/@category)) }</categories>
  </summary>
  {
    for $cat in distinct-values(//product/@category)
    let $products := //product[@category = $cat]
    order by $cat
    return <category name="{ $cat }" count="{ count($products) }"
                     avg-price="{ format-number(avg($products/price), '#.00') }"/>
  }
</report>

#Conditional Construction

Build elements conditionally:

<product>
  <name>{ $name }</name>
  { if ($description) then <description>{ $description }</description> else () }
  { if ($price > 100) then <badge>premium</badge> else () }
  {
    for $tag in $tags
    return <tag>{ $tag }</tag>
  }
</product>

The empty sequence () produces no output — this is how you conditionally include elements. Cleaner than wrapping everything in if/else blocks.

#Copy-Modify Pattern

Take an existing node and modify parts of it:

copy $order := //order[@id = "12345"]
modify (
  replace value of node $order/status with "shipped",
  insert node <shipped-date>{ current-date() }</shipped-date> as last into $order
)
return $order

This is XQuery Update Facility syntax — it creates a modified copy without changing the original. Useful for data transformations where most of the structure stays the same.