XSL: Copy input elements into output stream

The usual answer to the question “how do I copy input XML elements into output XSLT stream?” is “use xsl:copy or xsl:copy-of for deep copy.” But, as always, the devil is in the details. For example, if you're faced with a product catalog structure similar to this one …
<catalog>
  <product id="123">
    <name>Sample product</name>
    <text>A <strong>marvellous</strong>
      <span class='red'>red</span> book</text>
  </product>
</catalog>
… and use a simplistic approach to the copy-of
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:if test="text">
    <xsl:copy-of select="text" />
  </xsl:if>
</xsl:template>
… you'd end up with an extra non-HTML-compliant text node in the output stream …
<h3>Sample product</h3>
<text>A <strong>marvellous</strong>
 <span class="red">red</span> book </text>
To get just the contents you'd like to get, you should still use the xsl:copy-of instruction, but select just the child element (child::*) and the text nodes (text()) of the text element. To add icing on the cake, we'll enclose the contents of the text element in a P tag:
<xsl:template match="product">
  <h3><xsl:value-of select="name" /></h3>
  <xsl:for-each select="text">
    <p><xsl:copy-of select="text() | child::*" /></p>
  </xsl:for-each>
</xsl:template>

No comments:

Post a Comment