Our Man in Bananas
Our Man in Bananas

Reputation: 5981

prevent blank output lines at end of file in XSLT 1.0

We have an xml file sent by an external provider every day that looks like this:

<StreamStart>
    <Stream>
        ... data nodes
    </Stream>
</StreamStart>

<DDIVouchers>
    <Voucher>
        ... data nodes
    </Voucher>
</DDIVouchers>

<StreamEnd>
    <Stream>
        ... data nodes
    </Stream>
</StreamEnd>

My xslt is working fine, here it is:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:msg="http://www.voca.com/schemas/messaging" 
     xmlns:cmn="http://www.voca.com/schemas/common" 
     xmlns:str="http://exslt.org/strings" extension-element-prefixes="str" >
<xsl:output method="text" encoding="UTF-8" />
<xsl:strip-space elements="AccountName TransactionCode" />

    <!--
    defined template to convert YYYY-MM-DD into CYYMMDD 
        so 2014-08-14 becomes 1140814 which is good for Equation
    -->
 <xsl:template name="date">
    <xsl:param name="yyyy-mm-dd"/>
    <xsl:variable name="yyyy" select="substring-before($yyyy-mm-dd, '-')"/> <!-- extract the year-->
    <xsl:variable name="mm-dd" select="substring-after($yyyy-mm-dd, '-')"/> <!-- extract the month and day-->
    <xsl:variable name="mm" select="substring-before($mm-dd, '-')"/>            <!-- extract the month-->
    <xsl:variable name="dd" select="substring-after($mm-dd, '-')"/>             <!-- extract the day-->

    <!-- now determine if it will be 0 for before 2000, or 1 for after-->
    <xsl:choose>
      <xsl:when test="substring($yyyy,1,2)='19'">0</xsl:when>
      <xsl:otherwise>1</xsl:otherwise>
    </xsl:choose>
    <xsl:value-of select="substring($yyyy,3,4)"/>
    <xsl:value-of select="$mm"/>
    <xsl:value-of select="$dd"/>
  </xsl:template>

    <!--
    named template to first remove all the leading and trailing spaces, 
    then output the result with a total fixed length of 71 characters
    -->
   <xsl:template name="FormattedPhoneFaxNumber">
      <xsl:param name="text"/>
      <xsl:variable name="noSpaces" select="normalize-space($text)" />
      <xsl:value-of select="substring(concat('                                                                       ', $noSpaces), string-length($noSpaces) + 1)"/>
  </xsl:template>

<xsl:template match="/">
    <!--

    Explanation:
    The instruction:

        <xsl:for-each select="msg:VocaDocument/msg:Data/msg:Document/msg:DDIVouchers/msg:Voucher">

        puts us in the context of Voucher (within the default name-space that has been aliased to 'msg'. In this context, the path:

        "../../StreamStart/Stream/BankName"

        goes "up" to the parent element twice (i.e. to Document) and from there "down" to StreamStart/Stream/BankName where the required value is. 
string-length(../../msg:StreamEnd/msg:Stream/msg:StreamCode)
    -->

    <!-- START LOOP THROUGH ALL VOUCHERS-->
    <xsl:for-each select="msg:VocaDocument/msg:Data/msg:Document/msg:DDIVouchers/msg:Voucher">

        <!-- STREAM START-->
        <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:BankName, '                                                                      '), 1, 70)"/>
        <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:BankCode, '0000'), 1, 4)"/>
        <xsl:value-of select="substring(concat('0000', ../../msg:StreamStart/msg:Stream/msg:StreamCode), 3 ,4)"/>
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:VoucherSortCode"/>
        <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:VoucherAccountNumber, '00000000'), 1, 8)"/>        
        <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:TotalVouchers, '00000000'), 1, 8)"/>  

        <!-- VOUCHER START-->
        <xsl:value-of select="substring(concat(translate((msg:TransactionCode), ' ',''), '          '), 1, 10)"/> 
        <xsl:value-of select="substring(concat(msg:OriginatorIdentification/msg:ServiceUserName, '                                   '), 1, 35)"/>

        <xsl:value-of select="substring(concat(msg:OriginatorIdentification/msg:ServiceUserNumber, '      '), 1, 6)"/>
        <xsl:value-of select="substring(concat(msg:PayingBankAccount/msg:BankName, '                                                                      '), 1, 70)"/>
        <xsl:value-of select="substring(concat(normalize-space(msg:PayingBankAccount/msg:AccountName), '                  '), 1, 18)"/>
        <xsl:value-of select="substring(concat(normalize-space(msg:PayingBankAccount/msg:AccountNumber), '        '), 1, 8)"/>
        <xsl:value-of select="substring(concat(normalize-space(msg:PayingBankAccount/msg:UkSortCode), '        '), 1, 8)"/>
        <xsl:value-of select="substring(concat(normalize-space(msg:ReferenceNumber), '                  '), 1, 18)"/>

        <xsl:call-template name="FormattedPhoneFaxNumber">
            <xsl:with-param name="text" select="msg:ContactDetails/msg:PhoneNumber" />
        </xsl:call-template>

        <xsl:call-template name="FormattedPhoneFaxNumber">
            <xsl:with-param name="text" select="msg:ContactDetails/msg:FaxNumber" />
        </xsl:call-template>

        <!-- NOTE HOW TO EXTRACT AN ADDRESS ELEMENT THAT HAS ITS OWN NAMESPACE -->
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:AddresseeName, '                                 '), 1, 33)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:PostalName, '                                 '), 1, 33)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:AddressLine, '                                 '), 1, 33)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:TownName, '                              '), 1, 30)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:CountyIdentification, '                                        '), 1, 40)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:CountryName, '                                        '), 1, 40)"/>
        <xsl:value-of select="substring(concat(msg:ContactDetails/msg:Address/cmn:ZipCode, '          '), 1, 10)"/>

        <xsl:call-template name="date">
           <xsl:with-param name="yyyy-mm-dd" select="msg:ProcessingDate"/>
        </xsl:call-template>

        <xsl:value-of select="substring(concat(msg:BankAccount/msg:FirstLastVoucherCode, '               '), 1, 15)"/>
        <xsl:value-of select="substring(concat(msg:BankAccount/msg:AgencyBankCode, '0000'), 1, 4)"/>
        <xsl:value-of select="substring(concat(msg:BankAccount/msg:SortCode, '00000000'), 1, 8)"/>
        <xsl:value-of select="substring(concat(msg:BankAccount/msg:AccountNumber, '00000000'), 1, 8)"/>
        <xsl:value-of select="substring(concat('000000000000000', msg:BankAccount/msg:Counter), string-length(msg:BankAccount/msg:Counter) + 1, 15)"/>

        <!-- STREAM END-->
        <xsl:value-of select="substring(concat(../../msg:StreamEnd/msg:Stream/msg:BankName, '                                                                      '), 1, 70)"/>
        <xsl:value-of select="substring(concat(../../msg:StreamEnd/msg:Stream/msg:BankCode, '0000'), 1, 4)"/>
        <xsl:value-of select="substring(concat('0000', ../../msg:StreamEnd/msg:Stream/msg:StreamCode), 3 ,4)"/>
        <xsl:value-of select="substring(concat(../../msg:StreamEnd/msg:Stream/msg:VoucherSortCode, '00000000'), 1, 8)"/>
        <xsl:value-of select="substring(concat(../../msg:StreamEnd/msg:Stream/msg:VoucherAccountNumber, '00000000'), 1, 8)"/>
        <xsl:value-of select="substring(concat('00000000', ../../msg:StreamEnd/msg:Stream/msg:TotalVouchers), string-length(../../msg:StreamEnd/msg:Stream/msg:TotalVouchers) + 1, 8)"/>

        <!-- CR/LF NEW LINE &#xA;-->
        <xsl:text>&#xD;</xsl:text>

    </xsl:for-each>
</xsl:template> 

</xsl:stylesheet>

Here is the output showing the empty lines at the end:

enter image description here

In out output we are getting 2 empty lines at the end....and to me it seems that there is no reason for it.

is there something I can add or change in my XSLT stylesheet to prevent these extra blank lines at the end of our output (which is a fixed width text file that will be loaded into another accounting system.

EDIT:

I also tried adding these two templates at the very end of the stylesheet:

<xsl:template match="text()[not(string-length(normalize-space()))]"/>

<xsl:template match="text()[string-length(normalize-space()) > 0]">
  <xsl:value-of select="translate(.,'&#xA;&#xD;', '  ')"/>
</xsl:template>

(from SO:5737862 (answer by Dimitre) but I had no luck, or perhaps something else is wrong.

EDIT 2: Is it possible that the extra lines are the result of MS XMSL 6 DOM Document TransformNode method, which I am using in Excel VBA to generate the test file output?

Upvotes: 3

Views: 1518

Answers (1)

Tomalak
Tomalak

Reputation: 338316

You append a newline unconditionally:

<!-- CR/LF NEW LINE &#xA;-->
<xsl:text>&#xD;</xsl:text>

Try this instead (also note that a linefeed actually is &#xA;):

<xsl:if test="position () &lt; last()">
  <xsl:text>&#xA;</xsl:text>
</xsl:if>

Upvotes: 3

Related Questions