Hey Friends I've got another xsl/xpath problem. Sure a beginners problem, but I'm thinking still too "imperative".
<!-- XML -->
<Module>
<WideTeaserElement>
<Headline>Bla bla 1</Headline>
</WideTeaserElement>
<WideTeaserElement>
<Headline>Bla bla 2</Headline>
</WideTeaserElement>
</Module>
<!-- XSL -->
<!-- You are already in the Node Module -->
<xsl:template match="WideTeaserElement">
<ul class="TabbedPanelsTabGroup">
<xsl:for-each select=".">
<li class="TabbedPanelsTab"><a href="#"><xsl:valu开发者_运维问答e-of select="pub:Headline"/></a></li>
</xsl:for-each>
</ul>
</xsl>
And what I wanna get as output:
<ul class="TabbedPanelsTabGroup">
<li class="TabbedPanelsTab"><a href="#">Bla bla 1</a></li>
<li class="TabbedPanelsTab"><a href="#">Bla bla 2</a></li>
</ul>
With my XSL I get an output with two <ul>
Elements. So my question includes two things:
1) How has to be the transformation for my probleme above?
2) How can you handle loops like for-each in XSL, to have it more "under control"?
You almost never need to use for-each
in XSL, and if you find yourself using it, you're probably missing an important bit of XSL's power.
Your case could be much more idiomatically expressed by giving a template for the HeadLine
element (containing your <li>...</li>
element), and then using <xsl:apply-templates/>
within the <ul>
element within the WideTeaserElement
template.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match='Module'>
<ul class='TabbedPanelsTabGroup'>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match='Headline'>
<li class='TabbedPanelsTab'><a href='#'><xsl:apply-templates/></a></li>
</xsl:template>
</xsl:stylesheet>
This transformation (completely in the spirit of XSLT):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="Module">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="Headline">
<li class="TabbedPanelsTab"><a href="#"><xsl:value-of select="."/></a></li>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Module>
<WideTeaserElement>
<Headline>Bla bla 1</Headline>
</WideTeaserElement>
<WideTeaserElement>
<Headline>Bla bla 2</Headline>
</WideTeaserElement>
</Module>
produces the desired result:
<ul>
<li class="TabbedPanelsTab">
<a href="#">Bla bla 1</a>
</li>
<li class="TabbedPanelsTab">
<a href="#">Bla bla 2</a>
</li>
</ul>
The reason you're getting two ul
elements is because your template that generates the ul
element matches elements called WideTeaserElement
, of which you have two.
I think I can see where thinking was going with this- were you assuming that this template would process all WideTeaserElement
s in one operation, which you could then iterate through with 'for each of these'? Instead, the template is called separately for each occurrence of the WideTeaserElement
node, and then 'iterates' through the single occurrence of itself.
I agree with Norman that for-each
is rarely the best option, but I can think of two reasons why you might use it.
- Readability; if the amount of processing for each iteration is relatively small, it can make your stylesheet easier to read if it the code that handles it is within the parent template.
For (very simplified) example, I'd favor
<xsl:template match="mylist">
<xsl:element name="ul">
<xsl:for-each select="listitem">
<xsl:element name="li">
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
instead of
<xsl:template match="mylist">
<xsl:element name="ul">
<xsl:apply-templates select="listitem" />
</xsl:element>
</xsl:template>
<xsl:template match="listitem">
<xsl:element name="li">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
if the mylist
template actually had a lot more code, and the latter solution would mean having to scroll down my code to see how listitem
is handled. This is subjective though, some may prefer the latter solution always. Personally I usually find that most templates are large enough that breaking them up is better for readability, but in smaller ones this isn't always the case.
- Where you don't want the nodes your iterating through to be treated as they normally would by a template. In the above example, the latter solution would convert ALL
listitem
tags, not just those within a 'mylist' tag. It's possible to restrict it by changing the match tolistitem[parent::mylist]
of course, but often thefor-each
is just neater than making multiple templates for the same element name based on ancestry.
Generally speaking though, you can usually substitute
<xsl:template match="foo">
<xsl:for-each select="bar">
..
</xsl:for-each>
</xsl:template>
with
<xsl:template match="foo">
<xsl:apply-templates select="bar" />
</xsl:template>
<xsl:template match="bar">
..
</xsl:template>
in any document where a bar
element always has a foo
element as it's parent.
精彩评论