Ok, following on from my question here.
Lets say my pages are now like this:
<page>
<header>Page A</header>
<content-a>Random content for page A</content-a>
<content-b>More of page A's content</content-b>
<content-c>More of page A's content</content-c>
<!-- This doesn't keep going: there are a predefined number of sections -->
</page>
<page include="A.xml">
<header>Page B</header>
<content-a>Random content for page B</content-a>
<content-b>More of page B's content</content-b>
<content-c>More of page B's content</content-c>
</page>
<page include="B.xml">
<header>Page C</header>
<content-a>Random content for page C</content-a>
<content-b>More of page C's content</content-b>
<content-c>More of page C's content</content-c>
</page>
After the transform (on C.xml
), I'd like to end up with this:
<h1>Page C</h1>
<div>
<p>Random content for page C</p>
<p>Random content for page B</p>
<p>Random content for page A</p>
</div>
<div>
<p>More of page C's content</p>
<p>More of page B's content</p>
<p>More of page A's content</p>
</div>
<div>
<p>Yet more of page C's content</p>
<p>Yet more of page B's content</p>
<p>Yet more of page A's content</p>
</div>
I know that I can use document(@include)
to include another document. However, the recursion is a bit beyond me.
How would I go about writing such a transform?
Here is an XSLT 2.0 solution:
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="page">
<xsl:variable name="pages">
<xsl:apply-templates select="." mode="load" />
</xsl:variable>
<xsl:copy>
<h1><xsl:value-of select="header" /></h1>
<!-- you say there is a fixed number of names, so this should be OK -->
<xsl:for-each select="'content-a','content-b','content-c'">
<div>
<xsl:apply-templates select="$pages/page/*[name() = current()]" />
</div>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="page" mode="load">
<xsl:sequence select="." />
<xsl:apply-templates select="document(@include)" mode="load" />
</xsl:template>
<xsl:template match="content-a|content-b|content-c">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
EDIT: For XSLT 1.0, the equivalent solution would look like this:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
>
<xsl:template match="page">
<xsl:variable name="pages-rtf"><!-- rtf = result tree fragment -->
<xsl:apply-templates select="." mode="load" />
</xsl:variable>
<xsl:variable name="pages" select="exsl:node-set($pages-rtf)" />
<!-- you say there is a fixed number of names, so this should be OK -->
<xsl:variable name="nodes-rtf">
<content-a/><content-b/><content-c/>
</xsl:variable>
<xsl:variable name="nodes" select="exsl:node-set($nodes-rtf)" />
<xsl:copy>
<h1><xsl:value-of select="header" /></h1>
<xsl:for-each select="$nodes">
<div>
<xsl:apply-templates select="$pages/page/*[name() = name(current())]" />
</div>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="page" mode="load">
<xsl:copy-of select="." />
<xsl:apply-templates select="document(@include)" mode="load" />
</xsl:template>
<xsl:template match="content-a|content-b|content-c">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>