Chapter 5. XProc steps as XPath functions (in XSLT or XQuery)
In a blog post in May 2025 Norm Tovey-Walsh showed that it is useful to be able to call XProc steps as XPath functions. He as well implemented it with XMLCalabash. From rel 1.8.11 of MorganaXProc-III follows his lead and implements a subset of the original proposal: XProc step are callable as XPath functions within XSLT or XQuery. (Currently the implementation is restricted to Saxon-13, but more implementations are planned for the future.)
1. Mapping XProc steps to XPath functions
The basis idea is that if you have an XProc step declared as
1 |<p:declare-step type="test:example" xmlns:test="http://test">|<p:input port="source" content-types="xml" sequence="true" primary="true"/>|<p:input port="second" content-types="json" sequence="false" />|<p:option name="option1" as="xs:string*" />5 |<p:option name="option2" as="xs:boolean" />|<p:output port="result" />|</p:declare-step>
it can be called as an XPath function like
1 |test:example(|($doc1, $doc2), (:value sequence for port 'source':)|map{'key' : 'value'}, (:single value for port 'second':)|map{xs:QName('option1') : ('String-1', 'String-2'),5 |xs:QName('option2') : true()})
Every input port in the XProc step creates one argument of the XPath function in numeric order. If the step has options, the last function argument
is a map where the keys are xs:QNames representing the option name. The function's result is always a map with xs:NCNames as
keys representing the names of the step's output ports. The function's name is equal to the step's type-declaration (not it's name!). The correlation
between the input port's declaration and the function signature is as follows:
| content-types | sequence | parameter type |
|---|---|---|
| Any content-type not containing 'json' | sequence="true" | document-node()* |
| Any content-type not containing 'json' | sequence="false" | document-node() |
| Any content-type containing 'json' | sequence="true" | item()* |
| Any content-type containing 'json' | sequence="false" | item() |
Which steps are visible as functions depends on the pipeline: If the pipeline has a p:declare-step root element, the declared
step is available as a function provided it has a type attribute. If the root element is a p:library all steps declared
in this library having a type attribute are available via function call. Also all steps from imports directly to that library are available.
The visibility attribute currently is ignored (for now) so you can call steps marked as “private” as functions too.
2. Creating functions and calling them in Saxon 13
To use XProc steps from a pipeline or library in the XPath context of XSLT or XQuery, you have to use a Saxon startup hook
provided by class com.xml_project.morganaxproc3.saxon13connector.RegisterXProcStepsAsFunctions with -init on the
command line interface. You need to have MorganaXProc-IIIee.jar or MorganaXProc-IIIhe.jar
on the classpath starting Saxon. Additionally all relevant jar-files from MorganaXProc's
library folder have to be on the classpath. (see details in Configuration). At least you need to have
MorganaXProc-IIIee_lib/Saxon13Connector.jar on your classpath to make the function call work.
Additionally you have to use the following Java properties to make it work:
| Java property | Description |
|---|---|
| com.xml_project.morganaxproc.pipeline | provides a path tof the XProc pipeline to be compiled and called. The given path is urified and (if necessary) resolved against the CWD. |
| com.xml_project.morganaxproc.config | optionally you can use this property to name the path of a configuration file for MorganaXProc-III. Again the path is urified and (if necessary) resolved against the CWD. |
| com.xml_project.morganaxproc.statics | if the pipeline or library declares static options, you can use this property to provide a path to a document containing the values to be used. (For details see section 'Setting static options'). As with the other pathes the given value is urified and (if necessary) resovled against the CWD. |
The following example might help to clarify the necessary configuration (for Linux/MacOS). An adaption for Windows is pretty straight forward:
|MORGANA_HOME=$(dirname $CURRENT_SCRIPT)./vanilla-III/MorganaXProc-IIIee-1.8.11|MORGANA_PATH=$MORGANA_HOME/MorganaXProc-IIIee.jar|MORGANA_LIB=$MORGANA_HOME/MorganaXProc-IIIee_lib/*||SAXON_PATH=/users/berndzen/documents/software/JavaKlassen/SaxonHE13-0J/saxon-he-13.0.jar||CLASSPATH=$SAXON_PATH:$MORGANA_PATH:$MORGANA_LIB:$MORGANA_HOME/MorganaXProc-IIIee_lib/Saxon13Connector.jar||java -Dcom.xml_project.morganaxproc.config=morgana-config.xml \|-Dcom.xml_project.morganaxproc.pipeline=pipeline.xpl \|-Dcom.xml_project.morganaxproc.statics=statics-defs.xml \|-cp $CLASSPATH net.sf.saxon.Transform -s:source.xml -xsl:stylesheet.xsl \|-init:com.xml_project.morganaxproc3.saxon13connector.RegisterXProcStepsAsFunctions
In stylesheet stylesheet.xsl all the steps declared in pipeline.xpl are available as functions in all XPath expressions.