Contact Us 1-800-596-4880

Using XQuery with the XML Module - Mule 4

The <xml-module:xquery-transform> operation supports the evaluation of XQuery expressions.

Because XQuery expressions can match any number of individual elements, this operation returns a List of Strings. If no element matches the expression, the operation returns an empty list.

Note that though MuleSoft supports the XQuery standard, DataWeave is capable of achieving the same use cases and is the recommended tool for extracting and transforming XML documents.

This example shows how to use XQuery to manipulate a list of books:

Input: Book List
<?xml version="1.0" encoding="UTF-8"?>
<BOOKLIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="books.xsd">
    <BOOKS>
        <ITEM CAT="MMP">
            <TITLE>Pride and Prejudice</TITLE>
            <AUTHOR>Jane Austen</AUTHOR>
            <PUBLISHER>Modern Library</PUBLISHER>
            <PUB-DATE>2002-12-31</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>9</PRICE>
            <QUANTITY>187</QUANTITY>
            <ISBN>0679601686</ISBN>
            <PAGES>352</PAGES>
            <DIMENSIONS UNIT="in">8.3 5.7 1.1</DIMENSIONS>
            <WEIGHT UNIT="oz">6.1</WEIGHT>
        </ITEM>
        <ITEM CAT="P">
            <TITLE>Wuthering Heights</TITLE>
            <AUTHOR>Charlotte Bronte</AUTHOR>
            <PUBLISHER>Penguin Classics</PUBLISHER>
            <PUB-DATE>2002-12-31</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>9.040000000000001</PRICE>
            <QUANTITY>113</QUANTITY>
            <ISBN>0141439556</ISBN>
            <PAGES>430</PAGES>
            <DIMENSIONS UNIT="in">1.0 5.2 7.8</DIMENSIONS>
            <WEIGHT UNIT="oz">11.2</WEIGHT>
        </ITEM>
        <ITEM CAT="P">
            <TITLE>Tess of the d'Urbervilles</TITLE>
            <AUTHOR>Thomas Hardy</AUTHOR>
            <PUBLISHER>Bantam Classics</PUBLISHER>
            <PUB-DATE>1984-05-01</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>12.5</PRICE>
            <QUANTITY>85</QUANTITY>
            <ISBN>0553211684</ISBN>
            <PAGES>480</PAGES>
            <DIMENSIONS UNIT="in">6.8 4.2 0.8</DIMENSIONS>
            <WEIGHT UNIT="oz">7.7</WEIGHT>
        </ITEM>
        <ITEM CAT="P">
            <TITLE>Jude the Obscure</TITLE>
            <AUTHOR>Thomas Hardy</AUTHOR>
            <PUBLISHER>Penguin Classics</PUBLISHER>
            <PUB-DATE>1998-09-01</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>7</PRICE>
            <QUANTITY>129</QUANTITY>
            <ISBN>0140435387</ISBN>
            <PAGES>528</PAGES>
            <DIMENSIONS UNIT="in">7.8 5.2 0.9</DIMENSIONS>
            <WEIGHT UNIT="oz">10.9</WEIGHT>
        </ITEM>
        <ITEM CAT="H">
            <TITLE>The Big Over Easy</TITLE>
            <AUTHOR>Jasper Fforde</AUTHOR>
            <PUBLISHER>Hodder &amp; Stoughton</PUBLISHER>
            <PUB-DATE>2005-07-11</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>14.55</PRICE>
            <QUANTITY>129</QUANTITY>
            <ISBN>0340835672</ISBN>
            <PAGES>346</PAGES>
            <DIMENSIONS UNIT="cm">22.5 18.0 3.5</DIMENSIONS>
            <WEIGHT UNIT="g">390</WEIGHT>
        </ITEM>
        <ITEM CAT="P">
            <TITLE>The Eyre Affair</TITLE>
            <AUTHOR>Jasper Fforde</AUTHOR>
            <PUBLISHER>Penguin</PUBLISHER>
            <PUB-DATE>2003-02-25</PUB-DATE>
            <LANGUAGE>English</LANGUAGE>
            <PRICE>15</PRICE>
            <QUANTITY>129</QUANTITY>
            <ISBN>0142001805</ISBN>
            <PAGES>384</PAGES>
            <DIMENSIONS UNIT="in">7.8 5.0 0.9</DIMENSIONS>
            <WEIGHT UNIT="oz">9.0</WEIGHT>
        </ITEM>
    </BOOKS>
    <CATEGORIES DESC="Miscellaneous categories">
        <CATEGORY CODE="P" DESC="Paperback"/>
        <CATEGORY CODE="MMP" DESC="Mass-market Paperback"/>
        <CATEGORY CODE="H" DESC="Hard Cover"/>
    </CATEGORIES>
</BOOKLIST>

This example uses XQuery to transform the book list above:

XQuery Transformation Script
 <xml-module:xquery-transform>
    <xml-module:xquery><![CDATA[
        xquery version "3.0";
        declare copy-namespaces no-preserve, inherit;
        declare variable $document external;

        for $b in $document//BOOKS/ITEM
        order by string-length($b/TITLE) return
        <book>
            <author> { $b/AUTHOR } </author>
            <title> { $b/TITLE } </title>
        </book>
    ]]></xml-module:xquery>
</xml-module:xquery-transform>

The XQuery transformation produces a List of Strings in which each entry corresponds to each of the following <book> elements:

Output XML
<book>
   <author>
      <AUTHOR>Jasper Fforde</AUTHOR>
   </author>
   <title>
      <TITLE>The Eyre Affair</TITLE>
   </title>
</book>
<book>
   <author>
      <AUTHOR>Thomas Hardy</AUTHOR>
   </author>
   <title>
      <TITLE>Jude the Obscure</TITLE>
   </title>
</book>
<book>
   <author>
      <AUTHOR>Charlotte Bronte</AUTHOR>
   </author>
   <title>
      <TITLE>Wuthering Heights</TITLE>
   </title>
</book>
<book>
   <author>
      <AUTHOR>Jasper Fforde</AUTHOR>
   </author>
   <title>
      <TITLE>The Big Over Easy</TITLE>
   </title>
</book>
<book>
   <author>
      <AUTHOR>Jane Austen</AUTHOR>
   </author>
   <title>
      <TITLE>Pride and Prejudice</TITLE>
   </title>
</book>
<book>
   <author>
      <AUTHOR>Thomas Hardy</AUTHOR>
   </author>
   <title>
      <TITLE>Tess of the d'Urbervilles</TITLE>
   </title>
</book>

By default, the operation attempts to transform an XML document at the message payload level. However, you can use the content parameter to supply the input document:

Example: Using the content Parameter
<flow name="books">
    <file:read path="books.xml" target="books" />
    <xml-module:xquery-transform>
        <xml-module:content>#[vars.books]</xml-module:content>
        <xml-module:xquery><![CDATA[
            xquery version "3.0";
            declare copy-namespaces no-preserve, inherit;
            declare variable $document external;

            for $b in $document//BOOKS/ITEM
            order by string-length($b/TITLE) return
            <book>
                <author> { $b/AUTHOR } </author>
                <title> { $b/TITLE } </title>
            </book>
        ]]></xml-module:xquery>
    </xml-module:xquery-transform>
</flow>

The example above gets the content from elsewhere (in this case, from a file in the filesystem) and then references it through a simple expression.

Externalizing the XQuery Script to a Separate File

To avoid embedding the XQuery script in your Mule app, you can pass it to the module through a file, for example:

Example
<xml-module:xquery-transform>
    <xml-module:xquery>${file::scripts/books.xquery}</xml-module:xquery>
</xml-module:xquery-transform>

In a more complex use case, the script you use might depend on some external condition. For example, imagine a multi-tenant integration in which the actual transformation to use depends on a userID:

Complex Example
<flow name="multitenantExample">
    <http:listener path="transform" allowedMethods="POST" config-ref="httpListener" /> (1)
    <file:read path="#['xquery/$(attributes.queryParam.userId).xquery']" target="xquery" /> (2)
     <xml-module:xquery-transform>
        <xml-module:xquery>#[vars.xquery]</xml-module:xquery>
    </xml-module:xquery-transform>
</flow>
  1. This flow is triggered through an HTTP request.

  2. Assuming that the userID is provided as a query parameter, the example uses the File connector read the correct XQuery script and store it in a variable.

  3. The module executes a transformation using an expression that points to the XQuery script retrieved through the Read operation.

Note that the HTTP and File connectors are simply used as examples. They are not required to perform similar use cases.

Using Context Properties and Multiple Inputs

Like its xpath and xslt counterparts, the xquery operation supports context parameters that can pass arguments to the transformation.

For example, assume that you want to combine the books XML above with the following cities XML document:

Input
<?xml version="1.0" encoding="UTF-8"?>
<cities>
    <city name="milan" country="italy" pop="5"/>
    <city name="paris" country="france" pop="7"/>
    <city name="munich" country="germany" pop="4"/>
    <city name="lyon" country="france" pop="2"/>
    <city name="venice" country="italy" pop="1"/>
</cities>

Let’s look at an example for this use case:

XQuery Transformation Script
<flow name="multipleInputs">
  <file:read path="books.xml" target="books" />
  <file:read path="cities.xml" target="cities" /> (1)
  <xml-module:xquery-transform>
    <xml-module:xquery>
      <![CDATA[
          xquery version "3.0";
          declare variable $document external;
          declare variable $cities external; (2)
          declare variable $books external; (3)
          <mixes>
            {
              for $b in fn:doc($books)/BOOKLIST/BOOKS/ITEM,
                $c in fn:doc($cities)/cities/city (4)

              return <mix title="{$b/TITLE/text()}" city="{$c/@name}" /> (5)
            }
          </mixes>
      ]]>
    </xml-module:xquery>
    <xml-module:context-properties>
      #[{'books' : vars.books, 'cities': vars.cities}]
    </xml-module:context-properties> (6)
  </xml-module:xquery-transform>
</flow>
1 Read the input documents, and store the contents in variables. This example uses the File connector, but any data source is acceptable.
2 Declare the $cities variable in the XQuery script.
3 Declare the $books variable in the XQuery script.
4 Reference both documents using the $ prefix, and iterate both of them.
5 Produce the output elements.
6 Use DataWeave and the <xml-module:context-properties> parameter to bind the properties.

The script will output a List of Strings with only one item:

Output
<mixes>
   <mix title="Pride and Prejudice" city="milan"/>
   <mix title="Pride and Prejudice" city="paris"/>
   <mix title="Pride and Prejudice" city="munich"/>
   <mix title="Pride and Prejudice" city="lyon"/>
   <mix title="Pride and Prejudice" city="venice"/>
   <mix title="Wuthering Heights" city="milan"/>
   <mix title="Wuthering Heights" city="paris"/>
   <mix title="Wuthering Heights" city="munich"/>
   <mix title="Wuthering Heights" city="lyon"/>
   <mix title="Wuthering Heights" city="venice"/>
   <mix title="Tess of the d'Urbervilles" city="milan"/>
   <mix title="Tess of the d'Urbervilles" city="paris"/>
   <mix title="Tess of the d'Urbervilles" city="munich"/>
   <mix title="Tess of the d'Urbervilles" city="lyon"/>
   <mix title="Tess of the d'Urbervilles" city="venice"/>
   <mix title="Jude the Obscure" city="milan"/>
   <mix title="Jude the Obscure" city="paris"/>
   <mix title="Jude the Obscure" city="munich"/>
   <mix title="Jude the Obscure" city="lyon"/>
   <mix title="Jude the Obscure" city="venice"/>
   <mix title="The Big Over Easy" city="milan"/>
   <mix title="The Big Over Easy" city="paris"/>
   <mix title="The Big Over Easy" city="munich"/>
   <mix title="The Big Over Easy" city="lyon"/>
   <mix title="The Big Over Easy" city="venice"/>
   <mix title="The Eyre Affair" city="milan"/>
   <mix title="The Eyre Affair" city="paris"/>
   <mix title="The Eyre Affair" city="munich"/>
   <mix title="The Eyre Affair" city="lyon"/>
   <mix title="The Eyre Affair" city="venice"/>
</mixes>
View on GitHub