Martin Honnen
Martin Honnen

Reputation: 167716

How to expose functions defined in XSLT 2 stylesheet module to fn:transform?

The XPath 3.1 transform functions allows you to apply templates or call a named template or call a stylesheet function.

However, when I try to use an existing XSLT 2.0 stylesheet module (like, for instance, the functx module documented at http://www.xsltfunctions.com/ and available as XSLT 2 at http://www.xsltfunctions.com/xsl/functx-1.0-doc-2007-01.xsl) declaring lots of functions the XSLT 2 way within an xsl:stylesheet or xsl:transform root element, but not using the XSLT 3 xsl:package construct, I can't convince Saxon 9.8 or 9.9 to run any of the declared functions with the transform function and with the initial-function argument as I always get an error "XTDE0041: Cannot invoke function foo#2 externally, because it is not public".

Example could would be:

transform(
map {
'stylesheet-location': 'http://www.xsltfunctions.com/xsl/functx-1.0-doc-2007-01.xsl',
'delivery-format': 'raw',
'initial-function': QName('http://www.functx.com', 'substring-before-match'),
'function-params': ['abc-def-ghi', '[dg]']
})?output

but gives above cited error.

The only workaround I found is to construct (for example in XQuery) an XSLT 3 package on the fly importing the functx module and using xsl:expose to make the functions public:

let $functxNs := 'http://www.functx.com',
    $functxLoc := 'http://www.xsltfunctions.com/xsl/functx-1.0-doc-2007-01.xsl',
    $libPackage := <xsl:package
        name="http://example.com/import-functx"
        package-version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="3.0"
        xmlns:functx="http://www.functx.com">
        <xsl:expose
            component="function"
            names="functx:*"
            visibility="public"/>
        <xsl:import
            href="{$functxLoc}"/>
    </xsl:package>
return
    transform(
    map {
        'stylesheet-node': $libPackage,
        'delivery-format': 'raw',
        'initial-function': QName('http://www.functx.com', 'substring-before-match'),
        'function-params': ['abc-def-ghi', '[dg]']
    })?output

This works but requires using a host language to XPath 3.1 (like XQuery 3.1 above).

Is there any easier way to use the transform function and initial-function with existing XSLT 2 stylesheet modules declaring functions?

Upvotes: 0

Views: 279

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

Based on Michael Kay's explanatations in https://saxonica.plan.io/boards/3/topics/7540 any named function without a visibility attribute is by default considered private by the fn:transform function executing a stylesheet, whether the function appears in an explicit package or in an implicit one (i.e. a stylesheet module with xsl:stylesheet or xsl:transform).

Based on that one indeed needs to rewrite XSLT 2 code to add visibility attributes for functions supposed to be callable by an fn:transform execution or one needs to "wrap" the existing XSLT 2 code into an XSLT 3 construct that allows changing the visibility, like I did in my attempt to import the XSLT 2 code with xsl:import and to use xsl:expose to ensure the imported functions are made public by e.g. <xsl:expose component="function" names="functx:*" visibility="public"/>. The latter however requires use of XSLT 3 or XQuery 3 in addition to XPath 3.1's fn:transform.

Upvotes: 0

Related Questions