XSLT sometimes can be a pain in the bum, for sure it's not one of my favorite languages. :)
So today, I was trying to fix the following problem:
XML snippet looked like this:
<tag1> text 1 <tag11> <tag12>text 2</tag12> </tag11> text 3 <tag21> text 4 <tag22>text 5</tag22> </tag21> text 6 </tag1>
I wanted my output to look like this:
text 1 text 2 text 3 text 4 <a href="blah">text 5</a> text 6.
So pretty much what I needed to do was to get the text value of each node except from tag22 node, in which case I wanted to apply a template and output it as an anchor tag.
Here is the XSLT snippet which produces the desired result:
<xsl:apply-templates select="tag1" mode="foo"/> <xsl:template match="text()" mode="foo"> <xsl:value-of select"."/> </xsl:template> <xsl:template match="*" mode="foo"> <xsl:apply-templates mode="foo"/> </xsl:template> <xsl:template match="tag22" mode="foo"> <a href="blah"><xsl:value-of select="."/></a> </xsl:template>
Let me try to explain whats going on here.
- mode=foo : mode is a pretty usefull attribute with XSLT, if an xsl:apply-templates element has a mode attribute, then it applies only to those template rules from xsl:template elements that have a mode attribute with the same value; if an xsl:apply-templates element does not have a mode attribute, then it applies only to those template rules from xsl:template elements that do not have a mode attribute. We need the mode attribute in this case because, we need to override the default behavior when matching all tags under tag1.
- match=text() : this is a NodeType test, this instruction will be matched for text nodes, in which case we'll just output their values (text itself)
- match=* : this will initially match any node which is first level child of tag1. After that we recursevily call <xsl:apply-templates mode="foo">, to apply matching templates for all descendants of tag1's first level children. So we will keep applying templates until we reach a leaf, in which case we'll output its content.