Matt
Matt

Reputation: 3848

XSLT duplicating text nodes

I'm trying to figure out how to apply a styling element to different structure elements.

My XML looks like this:

 <?xml version="1.0" encoding="ISO-8859-1"?>
    <catalog>
        <cd>
            <title>Empire Burlesque</title>
            <artist><bold>Bob</bold> Dylan</artist>
        </cd>
        <cd>
            <title>Hide your <bold>heart</bold></title>
            <artist>Bonnie Tyler</artist>
        </cd>
    </catalog>

And my lastest XSLT attempt looks like this

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="bold">
<b><xsl:value-of select="." /></b>
</xsl:template>

<xsl:template match="/catalog">
  <html>
  <body>
    <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
      </tr>
      <xsl:for-each select="cd">
      <tr>
        <td><xsl:value-of select="title" /><xsl:apply-templates/></td>
        <td><xsl:value-of select="artist" /><xsl:apply-templates/></td>
      </tr>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>


</xsl:stylesheet>

But the result duplicates the data instead of just having the desired parts in bold.

Upvotes: 0

Views: 70

Answers (3)

Michael Kay
Michael Kay

Reputation: 163262

You're processing it once using xsl:value-of and then once again using xsl:apply-templates; if you don't want it processed twice, then just cut one of these out.

Upvotes: 0

Wayne
Wayne

Reputation: 60414

Here's another option that uses fewer explicit element matches. In many cases, this will be what you want, because it's simpler and more flexible. In some cases, this won't be what you want, because you'll want to handle each element individually. YMMV.

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/catalog">
        <html>
            <body>
                <h2>My CD Collection</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th>Title</th>
                        <th>Artist</th>
                    </tr>
                    <xsl:apply-templates />
                </table>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="cd">
        <tr><xsl:apply-templates /></tr>
    </xsl:template>
    <xsl:template match="cd/*">
        <td><xsl:apply-templates /></td>
    </xsl:template>
    <xsl:template match="bold">
        <b><xsl:apply-templates /></b>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Jim Garrison
Jim Garrison

Reputation: 86744

Here's what you need:

      <xsl:for-each select="cd">
        <tr>
          <td><xsl:apply-templates select="title"/></td>
          <td><xsl:apply-templates select="artist"/></td>
        </tr>
      </xsl:for-each>

Since you don't have any templates matching title or artist the default will be to copy their text nodes to the output. You don't need to do that yourself.

Here's a modification that uses more idiomatic "push" processing:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="bold">
    <b><xsl:value-of select="." /></b>
  </xsl:template>

  <xsl:template match="/catalog">
    <html>
      <body>
        <h2>My CD Collection</h2>
        <table border="1">
          <tr bgcolor="#9acd32">
            <th>Title</th>
            <th>Artist</th>
          </tr>
          <xsl:apply-templates select="cd"/>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="cd">
    <tr>
      <td><xsl:apply-templates select="title"/></td>
      <td><xsl:apply-templates select="artist"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions