Reputation: 25
I am building a blogging website for a school project. A blog post's content consists of a number of elements, each of which can either be plain text, links, images, or videos. All these elements must be shown in the order which they are stored with in the XML file, and each one will be displayed in a new <p>
tag.
Hence, the idea is to somehow loop all these elements, and each iteration should perform an `xsl:choose
to decide how to display the current iteration's element.
Take this code for example: how can I make it perform only the TEXT part when the element is a b:blogPostContent/b:blogPostText
type, and the LINK part when the element is a b:blogPostContent/b:blogPostLink
?
<xsl:for-each select="b:blogPostContent/*">
<p>
<!--TEXT-->
<xsl:value-of select="."/>
<!--LINK-->
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="./@target"/>
</xsl:attribute>
<xsl:attribute name="target">
_blank
</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
</p>
</xsl:for-each>
Upvotes: 2
Views: 3901
Reputation: 338108
Hence, the idea is to somehow loop all these elements, and each iteration should perform an
xsl:choose
to decide how to display the current iteration's element.
The idea is not bad, but driven by an imperative thought pattern. XSLT is not an imperative language and most of the time it's better to follow declarative patterns.
<xsl:template match="b:blogPostContent">
<article>
<xsl:apply-templates select="*" mode="wrap_p" />
</article>
</xsl:template>
<xsl:template select="*" mode="wrap_p">
<p>
<xsl:apply-templates select="." />
</p>
</xsl:template>
<xsl:template match="b:blogPostText">
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="b:blogPostLink">
<a href="{@target}" target="_blank">
<xsl:value-of select="."/>
</a>
</xsl:template>
<xsl:template match="b:blogPostList">
<ul>
<xsl:apply-templates select="*" />
</ul>
</xsl:template>
and so on.
This way you get small, dedicated, modular templates that do one thing and one thing only, while the XSLT engine decides which template serves best for which input node.
Note that you never have to write <xsl:element>
or <xsl:attribute>
unless you want to create them dynamically (names based on variables, for example). If you want an <a>
in the output, just put an <a>
in the XSLT.
Upvotes: 1
Reputation: 22617
Use a choose
element like this:
<xsl:choose>
<xsl:when test="name() = 'b:blogPostText'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="./@target"/>
</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
Another possiblity is to match those elements in separate templates. Let's say your template matches the b:blogPostContent
element (actually, you did not show the structure of your input XML):
<xsl:template match="b:blogPostContent">
<p>
<xsl:apply-templates select="b:blogPostText|b:blogPostLink"/>
</p>
</xsl:template>
<xsl:template match="b:blogPostText">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="b:blogPostLink">
<a>
<xsl:attribute name="href">
<xsl:value-of select="./@target"/>
</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="."/>
</a>
</xsl:template>
Note that <xsl:element name="a">
is exactly the same as <a>
. That is why I have shortened the notation. (It was inconsistent anyway, since you did not write <xsl:element name="p">
).
Upvotes: 1