Learn how to put your digital team to work with MuleSoft for Agentforce.
Contact Us 1-800-596-4880

XQuery Support

The XQuery Module gives users the ability to perform XQuery transformations on XML messages in Mule. This works in a very similar way to the XSLT Transformer shipped with Mule.

Mule 3.6.0 and later versions include XQuery 3.0 support for the XQuery transformer. The transformer’s behavior and syntax remain unaltered, and you can declare the version to use in the XQuery script itself (see below).

If you convert to Mule 3.6 and later, more information on XML can be found in XPath3 guide.

Declaring the XQuery Version

If the XQuery version is not declared, the transformer defaults to version 1.0. Version 3.0 is backwards-compatible and per the spec, all queries in 1.0 are valid in 3.0 and must return the same result.

To select the XQuery version to use, you can include it in the XQuery script, as shown below:

...
<mxml:xquery‐text>
<![CDATA[
  xquery version "3.0";
...

XQuery 3.0 introduces support for several new features in the XQuery transformer, such as using an XQuery script to operate on multiple documents at once. For more information, see XQuery 3.0 below.

Configuration

To use the XQuery transformer you need to add it to your Mule Xml configuration

<mxml:xquery-transformer name="xquery">
        <mxml:xquery-text>
           <![CDATA[
                declare variable $document external;
                declare variable $title external;
                declare variable $rating external;

                <cd-listings title='{$title}' rating='{$rating}'> {
                    for $cd in $document/catalog/cd
                    return <cd-title>{data($cd/title)}</cd-title>
                } </cd-listings>
            ]]>
        </mxml:xquery-text>

        <mxml:context-property key="title" value="#[mule.message:header('ListingTitle')]"/>
        <mxml:context-property key="rating" value="#[mule.message:header('ListingRating')]"/>

    </mxml:xquery-transformer>

Here we are configuring a transformer using in-line XQuery expressions.

We also define two <context-property> elements:

<mxml:context-property key="title" value="#[mule.message:header('ListingTitle')]"/>
<mxml:context-property key="rating" value="#[mule.message:header('ListingRating')]"/>

These properties are pulled from the current message and made available in the XQuery context so that they can be referenced in your XQuery statements. These can be object references or you can use Mule Expressions to get information from the current message.

Example

Now your configured XQuery transformer can be referenced by an endpoint. In the following example, you can drop and XML file into a directory on the local machine (see the inbound file endpoint) and the result will be written to System.out.

The test data looks like this:

<catalog>
    <cd>
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your heart</title>
        <artist>Bonnie Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
     ...
</catalog>

The result written to System.out looks like:

<cd-listings title="MyList" rating="6">
    <cd-title>Empire Burlesque</cd-title>
    <cd-title>Hide your heart</cd-title>
     ...
</cd-listings>

The full configuration for this examples looks like:

<mule xmlns="http://www.mulesource.org/schema/mule/core"
      xmlns:mxml="http://www.mulesource.org/schema/mule/xml"
      xmlns:vm="http://www.mulesource.org/schema/mule/vm"
      xmlns:stdio="http://www.mulesource.org/schema/mule/stdio"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
      http://www.mulesource.org/schema/mule/stdio http://www.mulesource.org/schema/mule/stdio/3.6/mule-stdio.xsd
      http://www.mulesource.org/schema/mule/vm http://www.mulesource.org/schema/mule/vm/3.6/mule-vm.xsd
      http://www.mulesource.org/schema/mule/xml http://www.mulesource.org/schema/mule/xml/3.6/mule-xml.xsd
       http://www.mulesource.org/schema/mule/core http://www.mulesource.org/schema/mule/core/3.6/mule.xsd">

    <mxml:xquery-transformer name="xquery">
        <mxml:xquery-text>
           <![CDATA[
                declare variable $document external;
                declare variable $title external;
                declare variable $rating external;

                <cd-listings title='{$title}' rating='{$rating}'> {
                    for $cd in $document/catalog/cd
                    return <cd-title>{data($cd/title)}</cd-title>
                } </cd-listings>
            ]]>
        </mxml:xquery-text>

        <mxml:context-property key="title" value="#[mule.message:header('ListingTitle')]"/>
        <mxml:context-property key="rating" value="#[mule.message:header('ListingRating')]"/>

    </mxml:xquery-transformer>

<flow name="testingFlow1" doc:name="testingFlow1">
        <vm:inbound-endpoint exchange-pattern="one-way" path="test.in" transformer-refs="xquery"/>
        <echo-component/>
        <all>
            <processor-chain>
                <vm:outbound-endpoint exchange-pattern="one-way"/>
            </processor-chain>
            <processor-chain>
                <outbound-endpoint doc:name="Generic"/>
            </processor-chain>
        </all>
    </flow>

</mule>

Testing XQuery

This can be tested using the following functional test.

public class XQueryFunctionalTestCase extends FunctionalTestCase
{
    protected String getConfigResources()
    {
        //Our Mule configuration file
        return "org/mule/test/integration/xml/xquery-functional-test.xml";
    }

    public void testMessageTransform() throws Exception
    {
        //We're using Xml Unit to compare results
        //Ignore whitespace and comments
        XMLUnit.setIgnoreWhitespace(true);
        XMLUnit.setIgnoreComments(true);

        //Read in src and result data
        String srcData = IOUtils.getResourceAsString("cd-catalog.xml", getClass());
        String resultData = IOUtils.getResourceAsString("cd-catalog-result-with-params.xml", getClass());

        //Create a new Mule Client
        MuleClient client = new MuleClient();

        //These are the message properties that pass into the XQuery context
        Map props = new HashMap();
        props.put("ListTitle", "MyList");
        props.put("ListRating", new Integer(6));

        //Invoke the flow
        MuleMessage message = client.send("vm://test.in", srcData, props);
        assertNotNull(message);
        assertNull(message.getExceptionPayload());
        //Compare results
        assertTrue(XMLUnit.compareXML(message.getPayloadAsString(), resultData).similar());
    }
}

XQuery 3.0

Mule 3.6.0 introduces support for XQuery 3.0. The XQuery transformer’s behavior and syntax remain unaltered, and you can declare the version to use in the XQuery script itself (see above).

Support for version 3.0 introduces some new features and improvements which are described in the following sections.

Operating on Multiple Inputs

Mule 3.6.0 introduces support for passing DOM documents and nodes (instances of org.w3c.dom.Document or org.w3c.dom.Node). The following simple query takes two XML files (one listing cities, the other listing books) and mixes the title of the book with the name of the city. The $cities and $books variables are passed as context properties.

<mxml:xquery‐transformer>
<mxml:context‐property key="books" value="#[flowVars['books']]" />
<mxml:context‐property key="cities" value="#[flowVars['cities']]" />
<mxml:xquery‐text>
<![CDATA[
    xquery version "3.0";
    declare variable $document external;
    declare variable $cities external;
    declare variable $books external;
    <mixes>
    {
        for $b in $books/BOOKLIST/BOOKS/ITEM,
            $c in $cities/cities/city
                return <mix title="{$b/TITLE/text()}" city="{$c/@name}" />
    }
    </mixes>
]]>
  </mxml:xquery‐text>
</mxml:xquery‐transformer>

Another possibility with XQuery 3.0 is to provide the path to the actual XML document, in which case the engine generates the document itself. In this case, the flow variables only contain the path to the XML documents on disk, and the fn:doc function inside the query does the parsing.

<mxml:xquery‐transformer>
    <mxml:context‐property key="books" value="#[flowVars['books']]" />
    <mxml:context‐property key="cities" value="#[flowVars['cities']]" />
    <mxml:xquery‐text>
    <![CDATA[
        xquery version "3.0";
        declare variable $document external;
        declare variable $cities external;
        declare variable $books external;
        <mixes>
        {
            for $b in fn:doc($books)/BOOKLIST/BOOKS/ITEM,
                $c in fn:doc($cities)/cities/city
                return <mix title="{$b/TITLE/text()}" city="{$c/@name}" />
        }
        </mixes>
    ]]>
  </mxml:xquery‐text>
</mxml:xquery‐transformer>

try…​ Catch Blocks

You can now use try …​ Catch blocks in your XQuery statements. This simple example shows a script which always fails and consistently returns an error tag.

<mxml:xquery‐transformer>
    <mxml:xquery‐text>
        <![CDATA[
            xquery version "3.0";
            declare variable $document external;
            let $x := "Hello"
            return
                try {
                    $x cast as xs:integer
                } catch * {
                    <error>Caught error {$err:code}: {$err:description}</error>
                }
        ]]>
  </mxml:xquery‐text>
</mxml:xquery‐transformer

Switch Statements

Switch blocks are now supported. The following example always returns <Paris />.

<mxml:xquery‐transformer>
<mxml:xquery‐text>
    <![CDATA[
      xquery version "3.0";
      let $animal := "France"
      return
        switch ($animal)
          case "Germany" return "Berlin"
          case "Spain" return "Madrid"
          case "France" return "Paris"
          case "England" case "UK" return "London"
          default return "What's the capital?"
    ]]>
  </mxml:xquery‐text>
</mxml:xquery‐transformer

Group By

XQuery 3.0 also introduces the group by clause, shown below:

<mxml:xquery‐transformer>
    <mxml:xquery‐text>
        <![CDATA[
            xquery version "3.0";
            declare variable $document external;
            for $n in 1 to 10
                group by $mod := $n mod 2
                return
                    if ($mod = 0) then
                        <even>{$n}</even>
                    else
                        <odd>{$n}</odd>
        ]]>
    </mxml:xquery‐text>
</mxml:xquery‐transformer>

The above script returns the following:

<odd>13579</odd>
<even>246810</even>

Return Type Improvements

Prior to Mule 3.6.0, the results returned by the XQuery transformer had several drawbacks:

  • By default, the transformer only returned the first result (unless the returnClass attribute was set to an array)

  • If the user specified a return value but no results were found, the transformer returned NullPayload

  • If only one result was found, the transformer returned that one element, even if it was configured to return an array

As of Mule 3.6.0, the default behavior has changed:

  • By default, the transformer returns a Java List

  • The list contains all results, even if only one was found

  • If no results are found, the list is empty

If you specify a return class, then the transformer falls back to the old behavior (array, one element, or null).