There are three essential elements to running a multi-pass transform.
<xsl:import>
<xsl:apply-imports>
node-set()
extension function
The example passes an XML document through two transforms serially. That is, it passes the content through a transform that removes namespace nodes and then takes the output from the first transform and passes it through a second transform that changes the title of the document. In this case the document is an XHTML document. The second transform is written such that it cannot accept an XHTML document with a namespace defined.
Original XHTML document
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>This is the old title</title>
</head>
<body>
<p>This is some text.</p>
</body>
</html>
After pass 1 (remove namespace nodes)
<html>
<head>
<title>This is the old title</title>
</head>
<body>
<p>This is some text.</p>
</body>
</html>
Result (after pass 2)
Note that the title text has changed.
<html>
<head>
<title>This is the new title</title>
</head>
<body>
<p>This is some text.</p>
</body>
</html>
XSLT for pass 1
This transform applies templates in this file to the content to remove namespace nodes, but copies the rest of the content. Then, during pass 2, applies the templates that are defined in the imported XSLT by using the <xsl:apply-imports>
tag. The templates for pass 2 are imported using the <xsl:import>
tag.
The results of the first pass are stored in a variable named "treefrag
". The tree fragment is converted to a node-set using the extension function "node-set()
". In this example, the Microsoft XML parser 4.0 is used, so the urn:schemas-microsoft-com:xslt
namespace is declared.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
>
<xsl:import href="pass2.xslt" />
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:param name="pass">1</xsl:param>
<xsl:choose>
<xsl:when test="$pass=1">
<xsl:variable name="treefrag">
<xsl:apply-templates>
<xsl:with-param name="pass" select="$pass" />
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="doc" select="
msxsl:node-set($treefrag)
" />
<xsl:apply-templates select="$doc">
<xsl:with-param name="pass">2</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$pass=2">
<xsl:apply-imports />
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- identity template without namespace nodes -->
<xsl:template match="*">
<xsl:param name="pass">2</xsl:param>
<xsl:choose>
<xsl:when test="$pass=1">
<xsl:element name="{name()}">
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="pass" select="$pass" />
</xsl:apply-templates>
</xsl:element>
</xsl:when>
<xsl:when test="$pass=2">
<xsl:apply-imports />
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="@*|text()|comment()|processing-instruction()">
<xsl:param name="pass">2</xsl:param>
<xsl:choose>
<xsl:when test="$pass=1">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="pass" select="$pass" />
</xsl:apply-templates>
</xsl:copy>
</xsl:when>
<xsl:when test="$pass=2">
<xsl:apply-imports />
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XSLT for pass 2
This transform simply changes the contents of the TITLE tag can copies all the rest of the content.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="title">
<title>This is the new title</title>
</xsl:template>
<!-- identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Source article: "XSLT: Multi-pass transforms"