3

I have a xml file which is like:

<Messages>
   error message 1

   error message 2 
</Messages>

I would like to have the output as:

Error 1: 
error message 1

Error 2:
error message 2

I'm using xslt 1.0, and I tried:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" omit-xml-declaration="yes"/>

<xsl:template match="Messages">
    <h3>Error 1:</h3>
    <xsl:value-of select="substring-before(./text(), '&#10;')"/>
    <h3>Error 2:</h3>
    <xsl:value-of select="substring-after(./text(), '&#10;')"/>
</xsl:template>
</xsl:stylesheet>

But it returned me nothing...Could anyone help me with this? Thanks!

2 Answers 2

1

You can use this recursive template to achieve this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" omit-xml-declaration="yes"/>

  <xsl:template match="Messages" name="outputError">
    <xsl:param name="index" select="1"/>
    <xsl:param name="source" select="text()"/>
    <xsl:variable name="thisLine" select="normalize-space(substring-before($source, '&#10;'))"/>
    <xsl:variable name="theRest" select="substring-after($source, '&#10;')"/>
    <xsl:choose>
      <xsl:when test="$thisLine">
        <h3>Error <xsl:value-of select="$index"/>:</h3>
        <span><xsl:value-of select="$thisLine"/></span>
        <xsl:call-template name="outputError">
          <xsl:with-param name="index" select="$index + 1"/>
          <xsl:with-param name="source" select="$theRest"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$theRest">
        <xsl:call-template name="outputError">
          <xsl:with-param name="index" select="$index"/>
          <xsl:with-param name="source" select="$theRest"/>
        </xsl:call-template>
      </xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

I wrapped the actual errors in a <span> element just to clearly separate them from whitespace, you can of course your a <p>, <div> or no element at all if you prefer.

Sign up to request clarification or add additional context in comments.

2 Comments

+1 for the $index parameter which makes the whole transformation more extensible~BTW, do you have any idea about why didn't my transformation work?
I suspect it's because your text actually contains a &#10; BEFORE the first error- the <Messages> open tag and the first error are on separate lines. My template will actually skip any empty lines, including the first one.
1

If I must use XSLT 1.0, I would write a two-pass transformation like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:variable name="vrtfPass1">
      <xsl:copy><xsl:apply-templates/></xsl:copy>
  </xsl:variable>

  <xsl:apply-templates select=
  "ext:node-set($vrtfPass1)/*/line
       [preceding-sibling::node()[1][self::text() and normalize-space()]]"/>
 </xsl:template>

 <xsl:template match="/*/text()" name="markUp">
  <xsl:param name="pText" select="."/>

  <xsl:if test="normalize-space($pText)">
      <xsl:value-of select="substring-before(concat($pText,'&#xA;'), '&#xA;')"/>
      <line/>
      <xsl:call-template name="markUp">
        <xsl:with-param name="pText" select="substring-after($pText,'&#xA;')"/>
      </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match="line">
Error: <xsl:value-of select="concat(position(), '&#xA;')"/>
  <xsl:value-of select="normalize-space(preceding-sibling::node()[1])"/>
  <xsl:text>&#xA;</xsl:text>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<Messages>
   error message 1

   error message 2
</Messages>

the wanted, correct result is produced:

Error: 1
error message 1

Error: 2
error message 2

2 Comments

Thank you for your answer, but I'm not getting it...For this "ext:node-set($vrtfPass1)/*/line [preceding-sibling::node()[1][self::text() and normalize-space()]]" expression, where does the 'line' come from? And what nodesets does variable $vrtfPass1 copy from?
@D.Q., line is an element name. line elements are generated in the first pass of the transformation. In XSLT 1.0 one generally needs to convert the captured result (of a pass of a transformation) from the infamous RTF (Result Tree Fragment) type, to a regular tree. This conversion is performed using a vendor-specific extension function (named usually node-set()) that resides in a vendor-defined namespace. This solution uses the EXSLT node-set() extension, because EXSLT is implemented by the majority of XSLT 1.0 processors and thus provides some degree of portability.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.