Reputation: 5394
I'm trying to learn XSLT (for some holiday coding fun). I think I now have a pretty good understanding of the basics (grabbing subtrees, filtering-out elements, and renaming elements). Where I'm having trouble is when it comes to drastically reorganizing XML structures. If you had a deeply nested structure and wanted to flatten it, how would you go about doing so?
For example, let's say I'm trying to transform a docbook snippet into html...
input (docbook):
<section>
<title>Title A</title>
<para>foo</para>
<para>bar</para>
<section>
<title>Title B</title>
<para>baz</para>
<para>biz</para>
<section>
<title>Title C</title>
<para>bing</para>
</section>
</section>
<section>
<title>Title D</title>
<para>fizzle</para>
</section>
</section>
output (html):
<h1>Title A</h1>
<p>foo</p>
<p>bar</p>
<h2>Title B</h2>
<p>baz</p>
<p>biz</p>
<h3>Title C</h3>
<p>bing</p>
<h2>Title D</h2>
<p>fizzle</p>
Is this where xsl:param
and xsl:call-template
come into play?
Thanks!
Upvotes: 2
Views: 2872
Reputation: 211
this should do the Job:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<html><header>
<xsl:apply-templates/>
</header></html>
</xsl:template>
<xsl:template match="title">
<h2><xsl:value-of select="." /></h2>
</xsl:template>
<xsl:template match="para">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
For flattening you don't need call-template.
If you use call-template
you hand over some attribs, see here
Upvotes: 1
Reputation: 8932
Carsten's test case works (with minor adjustments, you need terminate xsl:value-of
with a /) , but always uses <h2>
as the heading. If you want to use different heading elemenents according to the nesting level of the title, then you need something in addition to it:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title">
<xsl:choose>
<xsl:when test="count(ancestor::section) = 1">
<h1><xsl:value-of select="." /></h1>
</xsl:when>
<xsl:when test="count(ancestor::section) = 2">
<h2><xsl:value-of select="." /></h2>
</xsl:when>
<xsl:otherwise>
<h3><xsl:value-of select="." /></h3>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="para">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
The XPath function count(ancestor::section)
will return a count of all <section>
elements that are parents of the current element. In the example I have used <h1>
and <h2>
for the two outermost levels and <h3>
for anything deeper nested, but of course you can use other differentiations at your disgression.
It would even be possible to generate the number after the heading on the fly, using this expression:
<xsl:template match="title">
<xsl:variable name="heading">h<xsl:value-of select="count(ancestor::section)" /></xsl:variable>
<xsl:element name="{$heading}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
The xsl:variable
section in there creates a variable with a value of h
+ nesting level. The variable then can be used as a parameter for the xsl:element
element that allows you to dynamically define the name of the element you want to create.
Followup: If you want to use only the h1-h6 as suggested, you could do it like this:
<xsl:template match="title">
<xsl:variable name="hierarchy" select="count(ancestor::section)"/>
<xsl:variable name="heading">h<xsl:value-of select="$hierarchy" /></xsl:variable>
<xsl:choose>
<xsl:when test="$hierarchy > 6">
<h6 class="{$heading}"><xsl:value-of select="." /></h6>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$heading}">
<xsl:value-of select="." />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This expression uses <h6 class="h...">
for anything that has a nesting deeper than 6. It uses <h1>
through <h6>
for all other hierarchy levels.
Upvotes: 6