【问题标题】:prevent blank output lines at end of file in XSLT 1.0在 XSLT 1.0 中防止文件末尾出现空白输出行
【发布时间】:2014-09-05 14:42:24
【问题描述】:

我们每天都有一个外部提供商发送的 xml 文件,如下所示:

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

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

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

我的 xslt 工作正常,这里是:

<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>

这是显示末尾空行的输出:

在输出中,我们在最后得到 2 个空行......在我看来,这似乎没有理由。

我是否可以在我的 XSLT 样式表中添加或更改某些内容,以防止在我们的输出末尾出现这些额外的空白行(这是一个固定宽度的文本文件,将被加载到另一个会计系统中。

编辑:

我还尝试在样式表的最后添加这两个模板:

<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>

(来自SO:5737862 (answer by Dimitre),但我没有运气,或者可能有其他问题。

编辑 2: 额外的行是否可能是 MS XMSL 6 DOM Document TransformNode 方法的结果,我在 Excel VBA 中使用该方法生成测试文件输出?

【问题讨论】:

  • 两个问题:如果你使用&amp;#xA;而不是&amp;#xD;换行会发生什么?而且,如果你使用&lt;xsl:if test="position() &amp;lt; last()"&gt; 换行怎么办?
  • &amp;#xA; 是一样的。使用&lt;xsl:if test="position() &amp;lt; last()"&gt; 失败并出现“XML 错误”
  • 您确实关闭了 ,不是吗?
  • @Tomalak:这不应该是类似于&lt;xsl:if test="position() != last()"&gt; 的东西吗?
  • position() != last() 在这种情况下等同于 position() &lt; last()。您能否将输入的实时样本粘贴到 pastebin 或类似的东西中,我想调查一下。

标签: xml xslt-1.0 fixed-width


【解决方案1】:

无条件地追加一个换行符

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

试试这个(另请注意,换行符实际上是&amp;#xA;):

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

【讨论】:

  • 谢谢,解决了它 - 看起来它可以在 transformNode 方法上使用 eb MSXML。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多