tags:

views:

54

answers:

2

Using XSL I am trying to turn this XML:

<book><title>This is a <b>great</b> book</title></book>

into this XML:

<book>This is a <bold>great</bold> book</book>

using this xsl:

<xsl:for-each select="book/title/*">
<xsl:choose>
    <xsl:when test="name() = 'b'">
     <bold>
      <xsl:value-of select="text()"/>
     </bold>
    </xsl:when>
    <xsl:otherwise>
     <xsl:value-of select="text()"/>
    </xsl:otherwise>
</xsl:choose>
</xsl:for-each>

but my output is looking like this:

<book><bold>great</bold></bold>

Can anyone explain why the root text of <title> is getting lost? I believe my for-each select statement may need to be modified but I can't figure out what is should be.

Keep in mind that I cannot use an <xsl:template match> because of the complexity of my style sheet.

Thanks!

+7  A: 

This XPath expression:

book/title/*

means "all child elements of book/title". In your case, book/title has 3 child nodes:

  • Text node: This is a
  • Element node: <b>...</b>
  • Text node: book

As you can see, only one of them is an element, and gets selected. If you want to get all child nodes, both text and elements, use this:

book/title/node()

If you want to get text nodes separately, use this:

book/title/text()
Pavel Minaev
+1  A: 

While Pavel Minaev provided the answer to the question, it must be noted that this question demonstrates a really bad approach (probably due to lack of experience) to XSLT procesing.

The task can be accomplished in an elegant way, which demonstrates the power of XSLT:

When the above transformation is applied on the provided XML document:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes"/>
    <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="title">
      <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="title/b">
      <bold>
       <xsl:apply-templates/>
      </bold>
    </xsl:template>
</xsl:stylesheet>

the wanted result is produced:

<book><title>This is a <b>great</b> book</title></book>

This is a good illustration of one of the basic XSLT design patterns -- overriding the identity rule for elment renaming/flattening.

Dimitre Novatchev
Well, he stated that "keep in mind that I cannot use an `<xsl:template match>` because of the complexity of my style sheet" in the question, which I suspect means that he tried to do it with templates, and found that it clashed with existing rules. If so, `mode` should fix that.
Pavel Minaev
@pavel-minaev Probably he had poblems, but such bad code should not be let uncommented, or other people would think it was a good example :)
Dimitre Novatchev