XSLT - add

into text strings instead of \\n

2019-07-16 11:06发布

问题:

I have an XML document like this:

<xml>
  <item>
    <title>Article 1</title>
    <text><![CDATA[Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem diam, eleifend sed mollis id, condimentum in velit.

Sed sit amet erat ac mauris adipiscing elementum. Pellentesque eget quam augue, id faucibus magna.

Ut malesuada arcu eu elit sodales sodales. Morbi tristique porttitor tristique. Praesent eget vulputate dui. Cras ut tortor massa, at faucibus ligula.]]></text>
  </item>
</xml>

Where there are empty lines between the "paragraphs".

And I need to use XSLT transformation where the element will have each paragraph of text between < p > and < / p >. So my desired output would be like:

<h2>Article 1</h2>    
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lorem diam, eleifend sed mollis id, condimentum in velit.</p>
<p>Sed sit amet erat ac mauris adipiscing elementum. Pellentesque eget quam augue, id faucibus magna.</p>
<p>Ut malesuada arcu eu elit sodales sodales. Morbi tristique porttitor tristique. Praesent eget vulputate dui. Cras ut tortor massa, at faucibus ligula.</p>

So far I have an XSLT which looks like this:

<xsl:template match="/">
        <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
                <title>Page title</title>
            </head>
            <body>
                <h1>Table of contents</h1>
                <ol>
                <xsl:for-each select="xml/item>
                        <li><xsl:value-of select="./title"/></li>
                </xsl:for-each>
                </ol>
                <hr/>
                <xsl:for-each select="xml/item">
                    <h2><xsl:value-of select="./title"/></h2>
                    <xsl:value-of select="./text" disable-output-escaping="yes"/>
                </xsl:for-each>
            </body>
        </html>
</xsl:template>

How to deal with replacements of \n on the right places with paragraph HTML tags? I checked similar questions here, but I'm obviously not able to implement them to my problem.

回答1:

With XSLT 2.0, you can use a regular expression to tokenize your string.

To do so, replace your <xsl:value-of> by:

<xsl:analyze-string select="text" regex="&#xa;">
    <xsl:non-matching-substring>
        <p>
            <xsl:value-of select="."/>
        </p>
    </xsl:non-matching-substring>
</xsl:analyze-string>

In XSLT 1.0, you'd have to define a template that will be called recursively and generates a <p> element for the substring before the first linefeed.



回答2:

If you have to use XSLT 1.0, the following named template can do the job:

<xsl:template name="replace-nl">
  <xsl:param name="str"/>
  <xsl:if test="$str">
    <xsl:variable name="before" select="substring-before($str, '&#10;')"/>
    <xsl:variable name="after" select="substring-after($str, '&#10;')"/>      
    <p>
      <xsl:choose>
        <xsl:when test="$before">
          <xsl:value-of select="normalize-space($before)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="normalize-space($str)"/>
        </xsl:otherwise>
      </xsl:choose>
    </p>
    <xsl:call-template name="replace-nl">
      <xsl:with-param name="str" select="$after"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Just call it instead of xsl:value-of where you want the newline characters to be replaced, e.g.:

<xsl:for-each select="xml/item">
  ...
  <xsl:call-template name="replace-nl">
    <xsl:with-param name="str" select="text"/>
  </xsl:call-template>                    
</xsl:for-each>