[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] Truncating output of a node


Subject: Re: [xsl] Truncating output of a node
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 19 Apr 2001 23:17:40 +0100

Hi Jim,

> I guess another possibility it to output n characters and add '...'
> at the end to create the summary. Is that any easier?

I can just tell everyone's dying for a patented Jeni solution to this
problem. So here's my approach - step through the nodes one by one,
keeping track of the number of sentences that have been output so far
(as Mike suggested). Be careful, though - text nodes might hold more
than one sentence, so you need to have some recursion over the string
in there as well.

So have one template for text nodes:

<xsl:template match="text()" name="text">
   <!-- $sentences holds the number of sentences covered so far -->
   <xsl:param name="sentences" select="0" />
   <!-- $text holds the text of the text node (or portion of a text
        node when this template is called recursively -->
   <xsl:param name="text" select="." />
   <!-- $next holds the next node to move on to -->
   <xsl:param name="next" select="following-sibling::*[1]" />
   <xsl:choose>
      <!-- if this text contains a sentence delimiter... -->
      <xsl:when test="contains($text, '. ')">
         <!-- ... finish the sentence ... -->
         <xsl:value-of select="substring-before($text, '. ')" />
         <xsl:text>. </xsl:text>
         <!-- ... and if we've output less than 2 sentences before
              (i.e. we've not just finished the third) ... -->
         <xsl:if test="$sentences &lt; 2">
            <!-- ... call the template on the rest of the string ...
                 -->
            <xsl:call-template name="text">
               <!-- ... adding one to the sentences count -->
               <xsl:with-param name="sentences" select="$sentences + 1" />
               <!-- ... passing the rest of the text node -->
               <xsl:with-param name="text"
                               select="substring-after($text, '. ')" />
               <!-- ... and keeping the same next node -->
               <xsl:with-param name="next" select="$next" />
            </xsl:call-template>
         </xsl:if>
      </xsl:when>
      <xsl:otherwise>
         <!-- ... otherwise just give the value of the string ... -->
         <xsl:value-of select="$text" />
         <!-- ... and apply templates to the following span element,
              keeping the number of sentences the same -->
         <xsl:apply-templates select="$next">
            <xsl:with-param name="sentences" select="$sentences" />
         </xsl:apply-templates>
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

Have another template for span elements, which can be a lot simpler,
just basically applies templates to the next node, passing through the
same count - this takes advantage of the fact that sentences never end
within span elements:

<xsl:template match="span">
   <xsl:param name="sentences" select="0" />
   <xsl:copy-of select="." />
   <xsl:apply-templates select="following-sibling::node()[1]">
      <xsl:with-param name="sentences" select="$sentences" />
   </xsl:apply-templates>
</xsl:template>

Then you can start the process by just applying templates to the first
node under the summary:

<xsl:template match="summary">
   <xsl:apply-templates select="node()[1]" />
</xsl:template>

I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



Current Thread