Search code examples
xslturixqueryxslt-3.0

How do I run XQuery in XSLT 3.0?


I'm trying to find a way to run XQuery within XSLT as I have a large library of XQuery logic that I've been using for enriching XML payloads in a Oracle database and I now need to migrate away from using this database but still carryout the enrichment logic in a new platform. The platform I'm migrating to supports XSLT 3.0 so I'm interested to first find a way to be able to run XQuery within XSLT. I've understood this to be possible by using fn:load-xquery-module() and I'm currently trying to mock this up in Oxygen XML Editor 24.0 (as this ships with Saxon EE 9.9.1.7 which is same as my Server runtime) however I don't get it working so far.

My XQuery module (saved in the same directory as my XSLT with file name myfuncs.xq):

module namespace myfuncs = "http://example.com/functions";

declare function myfuncs:double($num as xs:double) as xs:double {
  $num * 2
};

declare function myfuncs:triple($num as xs:double) as xs:double {
  $num * 3
};

My XSLT that attempts to use the XQuery module above (note, I've desensitised the URI paths below, in my local implementation they report the full absolute path):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:myfuncs="http://example.com/functions"
    xmlns:fn="http://www.w3.org/2005/xpath-functions" 
    xmlns:saxon="http://saxon.sf.net/"
    exclude-result-prefixes="xs">
    
    <!-- Load the XQuery module -->
    <xsl:function name="myfuncs:double" as="xs:double">
        <xsl:param name="num" as="xs:double"/>
        <xsl:sequence select="fn:load-xquery-module('file:C:\Users\...\...\myfuncs.xq') ! myfuncs:double($num)"/>
    </xsl:function>
    
    <xsl:function name="myfuncs:triple" as="xs:double">
        <xsl:param name="num" as="xs:double"/>
        <xsl:sequence select="fn:load-xquery-module('file:C:\Users\...\...\myfuncs.xq') ! myfuncs:triple($num)"/>
    </xsl:function>
    
    <!-- Example usage -->
    <xsl:template match="/">
        <result>
            <double> <xsl:value-of select="myfuncs:double(5)"/> </double>
            <triple> <xsl:value-of select="myfuncs:triple(5)"/> </triple>
        </result>
    </xsl:template>
</xsl:stylesheet>

When I run this then I would expect my XQuery module to be loaded and myfuncs XQuery functions to be found and available for the XSLT, however instead I get the following error: Cannot locate module for namespace file:C:\Users...myfuncs.xq


Solution

  • My working sample for load-xquery-module I have is as follows:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
        xmlns:functx="http://www.functx.com"
        exclude-result-prefixes="xs math functx"
        version="3.0">
    
        <xsl:variable name="functx-module" select="load-xquery-module('http://www.functx.com', map { 'location-hints' : 'http://www.xqueryfunctions.com/xq/functx-1.0-doc-2007-01.xq' })"/>
    
        <xsl:template name="xsl:initial-template" match="/">
            <xsl:sequence select="$functx-module?functions(xs:QName('functx:word-count'))?1('This is a test.')"/>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Adapting that to your module (saved as module5.xqm in the same directory as the XSLT) gives

    <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xf="http://www.w3.org/2002/xforms"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:myfuncs="http://example.com/functions"
        exclude-result-prefixes="#all">
    
        <!-- Load the XQuery module -->
    
        <xsl:variable name="module5" select="load-xquery-module('http://example.com/functions', map { 'location-hints' : 'module5.xqm' })"/>
        
        <xsl:function name="myfuncs:double" as="xs:double">
            <xsl:param name="num" as="xs:double"/>
            <xsl:sequence select="$module5?functions(xs:QName('myfuncs:double'))?1($num)"/>
        </xsl:function>
        
        <xsl:function name="myfuncs:triple" as="xs:double">
            <xsl:param name="num" as="xs:double"/>
            <xsl:sequence select="$module5?functions(xs:QName('myfuncs:triple'))?1($num)"/>
        </xsl:function>
        
        <!-- Example usage -->
        <xsl:template match="/" name="xsl:initial-template">
            <result>
                <double> <xsl:value-of select="myfuncs:double(5)"/> </double>
                <triple> <xsl:value-of select="myfuncs:triple(5)"/> </triple>
            </result>
        </xsl:template>
    </xsl:stylesheet>