p:xslt (3.0)

Invoke an XSLT stylesheet.

Summary

<p:declare-step type="p:xslt">
  <input port="source" primary="true" content-types="any" sequence="true"/>
  <output port="result" primary="true" content-types="any" sequence="true"/>
  <input port="stylesheet" primary="false" content-types="xml" sequence="false"/>
  <output port="secondary" primary="false" content-types="any" sequence="true"/>
  <option name="global-context-item" as="item()?" required="false" select="()"/>
  <option name="initial-mode" as="xs:QName?" required="false" select="()"/>
  <option name="output-base-uri" as="xs:anyURI?" required="false" select="()"/>
  <option name="parameters" as="map(xs:QName,item()*)?" required="false" select="()"/>
  <option name="populate-default-collection" as="xs:boolean?" required="false" select="true()"/>
  <option name="static-parameters" as="map(xs:QName,item()*)?" required="false" select="()"/>
  <option name="template-name" as="xs:QName?" required="false" select="()"/>
  <option name="version" as="xs:string?" required="false" select="()"/>
</p:declare-step>

The p:xslt step invokes the XSLT stylesheet that appears on the stylesheet port. What exactly happens depends on the XSLT version used.

Ports:

Port

Type

Primary?

Content types

Seq?

Description

source

input

true

any

true

The source document(s) to transform. What exactly happens with these documents depends on the XSLT stylesheet version. See below.

result

output

true

any

true

The principal resulting document(s) of the transformation.

stylesheet

input

false

xml

false

The XSLT stylesheet to invoke.

secondary

output

false

any

true

Any secondary documents created by the transformation.

Starting with XSLT version 2.0, you can use the XSLT <xsl:result-document> instruction for this.

Options:

Name

Type

Req?

Default

Description

global-context-item

item()?

false

()

This explicitly sets the global context item for the XSLT stylesheet: the data the stylesheet starts working on. If you don’t use this option, the global context item is determined by what appears on the source port.

Setting the global context item is supported starting XSLT version 3.0.

initial-mode

xs:QName?

false

()

If this option is set, the XSLT stylesheet starts processing in the given mode.

Modes are supported starting XSLT version 2.0.

output-base-uri

xs:anyURI?

false

()

Explicitly sets the base URI for the stylesheet result(s). What exactly happens depends on the XSLT stylesheet version. See below.

parameters

map(xs:QName,item()*)?

false

()

A map with parameter-names and corresponding values to pass as global parameters to the XSLT stylesheet.

populate-default-collection

xs:boolean?

false

true

XSLT stylesheets have a default collection, accessible using the XPath collection() function. If you set this option to true, the documents appearing on the source port become the default collection.

Collections are supported starting XSLT version 2.0.

static-parameters

map(xs:QName,item()*)?

false

()

A map with parameter-names and corresponding values to pass as static parameters to the XSLT stylesheet.

Static stylesheet parameters are supported starting XSLT version 3.0.

template-name

xs:QName?

false

()

Usually, an XSLT stylesheet starts processing using “apply-template invocation”: it tries to find the most appropriate matching template and starts processing there. However, if the template-name option is set, a “call-template invocation” is performed: processing starts at that named template.

Starting processing at a named template is supported starting XSLT version 2.0.

version

xs:string?

false

()

Explicitly sets the XSLT stylesheet version. Probable values are 1.0, 2.0 or 3.0.

If this option is not set, officially the XSLT version used is implementation-defined and therefore depends on the XProc processor used. However, most likely the XProc processor will use the stylesheet version as indicated on the stylesheet root element (the xsl:stylesheet/@version or xsl:transform/@version attribute).

Description

The p:xslt step invokes the XSLT stylesheet that appears on the stylesheet port. What is used as input, how the XSLT processing starts and where/how the results appear depends on the XSLT version used. This is explained in the sections below.

Because of all the details, invoking the p:xslt step seems complicated. However, presumably, in the vast majority of cases it will be used in a classical manner: invoke an XSLT stylesheet on a source document and continue the pipeline using its result. Maybe with some parameters, maybe with some secondary results. For this, have a look at the Basic usage and Basic usage with secondary documents examples and don’t let all the details overwhelm you.

Specifying the XSLT version is important but, in most cases, rather simple: most likely the version as specified on the XSLT stylesheet root element (the xsl:stylesheet/@version or xsl:transform/@version attribute) is used. Since such a version attribute is required anyway, there usually won’t be anything special you need to do. However, if you want you can set the version explicitly using the version option.

Invoking an XSLT 3.0 stylesheet

If the stylesheet version is determined as 3.0, the following happens:

  • The parameters as set by the static-parameters option are passed to the stylesheet invocation as values for its static parameters.

  • An XSLT version 3.0 stylesheet has a global context item, the data the stylesheet works upon. This is determined as follows:

    • If the global-context-item option is set, this becomes the global context item.

    • If the global-context-item option is not set and a single document appears on the source port, this will become the global context item.

    • If the global-context-item option is not set and none or multiple documents appear on the source port, the global context item is absent/empty.

  • If the populate-default-collection is set to true, all documents that appear on the source port become the default collection, accessible using the XPath collection() function.

  • Then it is determined how to start the stylesheet processing:

    • If the template-name is not set, the normal “apply-template invocation” is performed. The document(s) that appear on the source port are used, one by one, for the initial match.

      If the initial-mode option is set, processing starts in that mode.

    • If the template-name is set, the named template with that name (<xsl:template name="…">) is invoked.

      The initial-mode option is ignored.

  • The stylesheet processes.

  • The result(s) appears on the output port(s):

    • All principal results of the stylesheet appear on the result port.

    • Any results created by <xsl:result-document> instructions appear on the secondary port.

  • Finally, the base URIs of the resulting documents (their base-uri document-property values) are determined. For this we first need to determine the base-output-URI:

    • If the base-output-uri option is set, this value is used as base-output-URI.

    • If the base-output-uri option is not set and there are documents on the source port, the base URI of the first document on the source port is used as base output URI.

    • If the base-output-uri option is not set and there are no documents on the source port, the base URI of the stylesheet is used as base-output-URI.

    The base URIs of the resulting documents (their base-uri document-property values) are now computed using this base-output-URI:

    • The base URI of the principal output document(s) becomes the base-output-URI. This means that when there are multiple principal documents, they all have the same base URI!

    • For all documents appearing on the secondary port, the base URI is determined by the xsl:result-document/@href attribute. A relative value is made absolute against the base-output-URI.

Invoking an XSLT 2.0 stylesheet

If the stylesheet version is determined as 3.0, the following happens:

  • The following options are ignored: static-parameters, global-context-item.

  • An XSLT version 2.0 stylesheet has an initial context node, the initial data the stylesheet works upon. This is determined as follows:

    • When no documents appear on the source port, the initial context node is undefined/empty.

    • When one or multiple documents appear on the source port, only the first document becomes the initial context node.

  • If the populate-default-collection is set to true, all documents that appear on the source port become the default collection, accessible using the XPath collection() function.

  • Then it is determined how to start the stylesheet processing:

    • If the template-name is not set, the normal “apply-template invocation” is performed. The document(s) that appear on the source port are used, one by one, for the initial match.

      If the initial-mode option is set, processing starts in that mode.

    • If the template-name is set, the named template with that name (<xsl:template name="…">) is invoked.

      The initial-mode option is ignored.

  • The stylesheet processes.

  • The result(s) appears on the output port(s):

    • The principal result document of the stylesheet appears on the result port.

    • Any results created by <xsl:result-document> instructions appear on the secondary port.

  • Finally, the base URIs of the resulting documents (their base-uri document-property values) are determined. For this we first need to determine the base-output-URI:

    • If the base-output-uri option is set, this value is used as base-output-URI.

    • If the base-output-uri option is not set and there are documents on the source port, the base URI of the first document on the source port is used as base output URI.

    • If the base-output-uri option is not set and there are no documents on the source port, the base URI of the stylesheet is used as base-output-URI.

    The base URIs of the resulting documents (their base-uri document-property values) are now computed using this base-output-URI:

    • The base URI of the principal output document becomes the base-output-URI.

    • For all documents appearing on the secondary port, the base URI is determined by the xsl:result-document/@href attribute. A relative value is made absolute against the base-output-URI.

Invoking an XSLT 1.0 stylesheet

If the stylesheet version is determined as 1.0, the following happens:

  • The following options are ignored: global-context-item, initial-mode, populate-default-collection, static-parameters, template-name.

  • There must be exactly one document appearing on the source port. This document will be processed.

  • The stylesheet processes.

  • The resulting document appears on the result port. The secondary port will always be empty.

  • Finally, the base URI of the resulting document (its base-uri document-property value) is determined. For this we first need to determine the base-output-URI:

    • If the base-output-uri option is set, this value is used as base-output-URI.

    • If the base-output-uri option is not set, the base URI of the first document on the source port is used as base-output-URI.

    The base URI of the output document becomes the base-output-URI.

Examples

Basic usage

For the following example, we’ll use a very simple (3.0) stylesheet, called add-comment.xsl, that adds a comment as the first child of the root element:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" expand-text="true">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:param name="comment-text" as="xs:string" required="false" select="'This is an added comment'"/>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:comment> == {current-dateTime()} - {$comment-text} == </xsl:comment>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Running this without any bells and whistles is as follows:

Source document:

<customers>
   <customer>
      <name>PXSLT Company Ltd</name>
   </customer>
</customers>

Pipeline document:

<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" version="3.0">

  <p:input port="source"/>
  <p:output port="result"/>

  <p:xslt>
    <p:with-input port="stylesheet" href="add-comment.xsl"/>
  </p:xslt>

</p:declare-step>

Result document:

<customers><!-- == 2024-12-02T13:40:21.0310538+01:00 - This is an added comment == -->
   <customer>
      <name>PXSLT Company Ltd</name>
   </customer>
</customers>

Setting a stylesheet parameter is done by supplying a map with parameter name/value pairs as the value of the parameters option:

<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" version="3.0">

  <p:input port="source"/>
  <p:output port="result"/>

  <p:xslt parameters="map{'comment-text': 'Special comment text by parameter!'}">
    <p:with-input port="stylesheet" href="add-comment.xsl"/>
  </p:xslt>

</p:declare-step>

Result document:

<customers><!-- == 2024-12-02T13:40:21.0878627+01:00 - Special comment text by parameter! == -->
   <customer>
      <name>PXSLT Company Ltd</name>
   </customer>
</customers>

Basic usage with secondary documents

The output of <xsl:result-document> stylesheet instructions is written to the secondary port of the p:xslt invocation. The base URI of these documents is the value of the xsl:result-document/@href attribute.

The following stylesheet, called split-documents.xsl, writes the contents of each <document> element to a separate, secondary, document. The base URI of the output documents is inferred from the document/@name attribute. The primary output of the stylesheet is almost identical to its input: the full URI of each written secondary output document is added to the <document> element in an href attribute.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" expand-text="true">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="document">
    <xsl:variable name="href" as="xs:string" select="resolve-uri('tmp/' || @name)"/>
    <xsl:result-document href="{$href}">
      <xsl:sequence select="*"/>
    </xsl:result-document>
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:attribute name="href" select="$href"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

When using this stylesheet in an XProc pipeline, the documents, written by <xsl:result-document> instructions, end up on the secondary port. If we want these written to disk, we need to add some code for it.

Source document:

<documents>
   <document name="x1.xml">
      <document-1/>
   </document>
   <document name="x2.xml">
      <document-2/>
   </document>
</documents>

Pipeline document:

<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" version="3.0">

  <p:input port="source"/>
  <p:output port="result" pipe="result@create-secondary-documents"/>

  <p:xslt name="create-secondary-documents">
    <p:with-input port="stylesheet" href="split-documents.xsl"/>
  </p:xslt>

  <p:for-each>
    <p:with-input pipe="secondary"/>
    <p:store href="{base-uri(/)}"/>
  </p:for-each>

</p:declare-step>

Result document:

<documents>
   <document name="x1.xml" href="file:/…/…/tmp/x1.xml">
      <document-1/>
   </document>
   <document name="x2.xml" href="file:/…/…/tmp/x2.xml">
      <document-2/>
   </document>
</documents>

The primary output of the p:xslt is explicitly piped to the output port of the pipeline here (by the p:output/@pipe attribute).

The p:for-each after the p:xslt iterates over all secondary documents and invokes p:store to store them to disk, using their base URI, that was set by the stylesheet. The result will be two documents, called x1.xml and x2.xml, in the tmp/ folder underneath the stylesheet location.

Additional details

  • Which XSLT version(s) is/are supported is implementation-defined and therefore depends on the XProc processor used. In most cases at least version 3.0 will be supported.

  • No document-properties from the source document(s) are preserved.

  • The base-uri document of each result document (both for the result and the secondary port) is determined by the transformation. If the transformation does not establish a base URI, the document will not have a base-uri document-property.

  • If the template-name option is set, the initial-mode option is ignored.

  • A relative value for the output-base-uri option is made absolute against the base URI of the element in the pipeline it is specified on. In most cases this will be the path of the pipeline document.

  • An XSLT stylesheet can terminate processing using an <xsl:message terminate="true"> instruction. How such a termination is reported by the XProc processor is implementation-defined and therefore depends on the XProc processor used.

  • The order in which result documents appear on the secondary port is implementation-defined and therefore depends on the XProc processor used.

Errors raised

Error code

Description

XC0007

It is a dynamic error if any key in parameters is associated to a value which is not an instance of the XQuery 1.0 and XPath 2.0 Data Model, e.g. with a map, an array, or a function.

XC0008

It is a dynamic error if the stylesheet does not support a given mode.

XC0038

It is a dynamic error if the specified xslt version is not available.

XC0039

It is a dynamic error if the source port does not contain exactly one XML document or one HTML document if XSLT 1.0 is used.

XC0056

It is a dynamic error if the stylesheet does not provide a given template.

XC0093

It is a dynamic error if a static error occurs during the static analysis of the XSLT stylesheet.

XC0094

It is a dynamic error if any document supplied on the source port is not an XML document, an HTML documents, or a Text document if XSLT 2.0 is used.

XC0095

It is a dynamic error if an error occurred during the transformation.

XC0096

It is a dynamic error if the transformation is terminated by XSLT message termination.

XC0105

It is a dynamic error if an XSLT 1.0 stylesheet is invoked and option parameters contains a value that is not an atomic value or a node.

XC0121

It is a dynamic error if a document appearing on the secondary port has a base URI that is not both absolute and valid according to RFC 3986 .

Reference information

This description of the p:xslt step is for XProc version: 3.0. This is a required step (an XProc 3.0 processor must support this).

The formal specification for the p:xslt step can be found here.

The p:xslt step is part of categories:

The p:xslt step is also present in version: 3.1.