tags:

views:

350

answers:

1

How do I apply a change to text in an element without losing its child elements.

For example:

I have this xml that I would like to apply a change to the text inside the "p" element....

<section>
    <p >Awesome LO</p>
    <p >
     Begin with an interesting fact, thought-provoking
     <keyword>question</keyword>
     <context>
      <p type="Key Words Head">Banana</p>
      <p type="Key Words">A tasty treat to eat any time, and good with ice cream – a banana split.</p>
     </context>, or a one sentence scenario to illustrate why the learning object (content) is important.
    </p>
    <p >
     Begin with a definition, if required. Then, provide an example by example view.
    </p>
</section>

So my xsl looks like this....

<xsl:template match="p">
    <xsl:copy>
     <xsl:call-template name="widont-title">
      <xsl:with-param name="text" select="text()" />
     </xsl:call-template>
    </xsl:copy>
</xsl:template>

The problem is that I lose the "keyword", "context", and other elements inside of the "p" when I do this. Can anyone point me to any clues? Thanks!

<!-- this method puts a non breaking space in the last word of a 'p' if its less than 5 characters-->
<xsl:template name="widont-title">
    <xsl:param name="temp"/>
    <xsl:param name="text"/>
    <xsl:param name="minWidowLength" select="5"/>

    <xsl:choose>
     <xsl:when test="contains($text, ' ')">
      <xsl:variable name="prev" select="substring-before($text,' ')"/>
      <xsl:variable name="before" select="concat($temp,' ',$prev)"/>
      <xsl:variable name="after" select="substring-after($text, ' ')"/>

      <xsl:choose>
       <xsl:when test="contains($after, ' ')">
        <xsl:call-template name="widont-title">
         <xsl:with-param name="temp" select="$before"/>
         <xsl:with-param name="text" select="$after"/>
        </xsl:call-template>
       </xsl:when>
       <xsl:when test="not(contains($after, ' ')) and string-length(translate($after,'`~!@#$%^\*()-_=+\\|]}[{;:,./?&lt;&gt;','')) &lt; $minWidowLength">

        <xsl:value-of select="concat($before, '&#160;', $after)" />
       </xsl:when>
       <xsl:otherwise>
        <xsl:value-of select="concat($before, ' ', $after)" />
       </xsl:otherwise>
      </xsl:choose>
     </xsl:when>
     <xsl:otherwise>
      <xsl:value-of select="$text"/>
     </xsl:otherwise>
    </xsl:choose>
</xsl:template>
+4  A: 

It is not clear what the widont-title template does and whether this is correctly implemented (looks a little-bit too-complicated), but the problem is that this template is applied too-soon, not leaving any possibilities for children elements of p to be processed.

The solution (using the identity template and overriding it for p/text() nodes) is very simple:

<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="p/text()">
     <xsl:call-template name="widont-title">
      <xsl:with-param name="text" select="." />
     </xsl:call-template>
    </xsl:template>

         <!-- "widont-title" template omitted for brevity -->

</xsl:stylesheet>

when the above transformation is applied on the provided XML document, the children of any p element are correctly present in the output.

Dimitre Novatchev