Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split text and preserve HTML tags (XSLT 2.0)

I have an xml that has a description node:

<config>
  <desc>A <b>first</b> sentence here. The second sentence with some link <a href="myurl">The link</a>. The <u>third</u> one.</desc>
</config>

I am trying to split the sentences using dot as separator but keeping in the same time in the HTML output the eventual HTML tags. What I have so far is a template that splits the description but the HTML tags are lost in the output due to the normalize-space and substring-before functions. My current template is given below:

<xsl:template name="output-tokens">
  <xsl:param name="sourceText" />

  <!-- Force a . at the end -->
  <xsl:variable name="newlist" select="concat(normalize-space($sourceText), ' ')" />
  <!-- Check if we have really a point at the end -->
  <xsl:choose>
    <xsl:when test ="contains($newlist, '.')">
      <!-- Find the first . in the string -->
      <xsl:variable name="first" select="substring-before($newlist, '.')" />

      <!-- Get the remaining text -->
      <xsl:variable name="remaining" select="substring-after($newlist, '.')" />
      <!-- Check if our string is not in fact a . or an empty string -->
      <xsl:if test="normalize-space($first)!='.' and normalize-space($first)!=''">
        <p><xsl:value-of select="normalize-space($first)" />.</p>
      </xsl:if>
      <!-- Recursively apply the template for the remaining text -->
      <xsl:if test="$remaining">
        <xsl:call-template name="output-tokens">
          <xsl:with-param name="sourceText" select="$remaining" />
        </xsl:call-template>
      </xsl:if>
    </xsl:when>
    <!--If no . was found -->
    <xsl:otherwise>
      <p>
        <!-- If the string does not contains a . then display the text but avoid 
           displaying empty strings 
         -->
        <xsl:if test="normalize-space($sourceText)!=''">
          <xsl:value-of select="normalize-space($sourceText)" />.
        </xsl:if>
      </p>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

and I am using it in the following manner:

<xsl:template match="config">
  <xsl:call-template name="output-tokens">
       <xsl:with-param name="sourceText" select="desc" />
  </xsl:call-template>
</xsl:template>

The expected output is:

<p>A <b>first</b> sentence here.</p>
<p>The second sentence with some link <a href="myurl">The link</a>.</p>
<p>The <u>third</u> one.</p>
like image 777
Lycaon Avatar asked Jan 19 '23 10:01

Lycaon


1 Answers

A good question, and not an easy one to solve. Especially, of course, if you're using XSLT 1.0 (you really need to tell us if that's the case).

I've seen two approaches to the problem. Both involve breaking it into smaller problems.

The first approach is to convert the markup into text (for example replace <b>first</b> by [b]first[/b]), then use text manipulation operations (xsl:analyze-string) to split it into sentences, and then reconstitute the markup within the sentences.

The second approach (which I personally prefer) is to convert the text delimiters into markup (convert "." to <stop/>) and then use positional grouping techniques (typically <xsl:for-each-group group-ending-with="stop"/> to convert the sentences into paragraphs.)

like image 57
Michael Kay Avatar answered Jan 28 '23 05:01

Michael Kay