0

I have to convert xml data into fixed length text file using XSLT 2.0.

I could use something like <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter, ' '), 1, 10)"/> which works, but with 20 columns, all of them to be output to different lengths I thought it'd be more elegant to use a global function to concatenate the strings and return the substring.

So I wrote this:

<xsl:function name="func:padStr">
    <xsl:param name="str"/> 
    <xsl:param name="chr"/> 
    <xsl:param name="len"/> 
    <xsl:value-of select="substring(concat($str,$chr),1,$len)"/>
</xsl:function>

which compiles under xslt 2.0 fine, but when I try to use it my xsl fails.

I have tried this to pad the above string value but it fails every time and I don't know what to try next:

Here is my complete XML:

<xsl:stylesheet version="2.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" >
<xsl:output method="text" encoding="UTF-8" />

<xsl:function name="func:padStr">
    <xsl:param name="str"/> 
    <xsl:param name="chr"/> 
    <xsl:param name="len"/> 
    <xsl:value-of select="substring(concat($str,$chr),1,$len)"/>
</xsl:function>

<xsl:template match="/">
    <xsl:for-each select="msg:VocaDocument/msg:Data/msg:Document/msg:DDIVouchers/msg:Voucher">
        <xsl:value-of select="substring(concat(../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter, '          '), 1, 10)"/>

        <!-- THIS CAUSES A FAIL -->    
        <xsl:value-of select="func:padStr('../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter', ' '), 1, 10)"/>

        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:BankName" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:BankCode" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:AgencyBankName" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:AgencyBankCode" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:StreamCode" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:VoucherSortCode" />
        <xsl:value-of select="../../msg:StreamStart/msg:Stream/msg:VoucherAccountNumber" />
        <xsl:value-of select="msg:BankAccount/msg:SortCode" />
        <xsl:value-of select="msg:BankAccount/msg:AccountNumber" />
        <xsl:value-of select="msg:BankAccount/msg:TotalVouchers" />

        <!-- NOTE HOW TO EXTRACT AN ADDRESS ELEMENT THAT HAS ITS OWN NAMESPACE -->
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:AddresseeName" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:PostalName" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:AddressLine" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:TownName" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:CountyIdentification" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:CountryName" />
        <xsl:value-of select="msg:ContactDetails/msg:Address/cmn:ZipCode" />

        <xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:for-each>
</xsl:template> 

</xsl:stylesheet>

here (for completeness) is my xml:

<VocaDocument xmlns:cmn="http://www.voca.com/schemas/common"  xmlns="http://www.voca.com/schemas/messaging" xmlns:iso="http://www.voca.com/schemas/common/iso" xmlns:env="http://www.voca.com/schemas/envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.voca.com/schemas/messaging http://www.voca.com/schemas/messaging/Voca_AUDDIS_AdviceofDDI_v1.0.xsd">
  <Data>
      <Document>
          <StreamStart>
              <Stream>
           <AgencyBankParameter>234</AgencyBankParameter>
          <BankName>LLOYDS BANK PLC</BankName>
          <BankCode>0004</BankCode>
          <AgencyBankName>BANK OF CYPRUS UK LTD</AgencyBankName>
          <AgencyBankCode>0234</AgencyBankCode>
          <StreamCode>01</StreamCode>
          <VoucherSortCode>SC300037</VoucherSortCode>
          <VoucherAccountNumber>46990760</VoucherAccountNumber>              
          </Stream>
          </StreamStart>
          <DDIVouchers>
              <Voucher>
                  <TransactionCode>NEW</TransactionCode> 
                  <OriginatorIdentification>
                      <ServiceUserNumber>123456</ServiceUserNumber> 
                  </OriginatorIdentification>
               </Voucher>              
               <Voucher>
                  <TransactionCode>OLD</TransactionCode> 
                  <OriginatorIdentification>
                      <ServiceUserNumber>789012</ServiceUserNumber> 
                  </OriginatorIdentification>
                  <ContactDetails>
            <PhoneNumber>020 83395862</PhoneNumber>
            <FaxNumber> FAX</FaxNumber>
              <Address>
                  <cmn:AddresseeName>RANALD LESLIE</cmn:AddresseeName>
                  <cmn:PostalName>NUFFIELD HEALTH </cmn:PostalName>
                  <cmn:AddressLine>NUFFIELD HOUSE</cmn:AddressLine>
                  <cmn:TownName>SURBITON</cmn:TownName>
                  <cmn:CountyIdentification> </cmn:CountyIdentification>
                  <cmn:CountryName>UNITED KINGDOM</cmn:CountryName>
                  <cmn:ZipCode>KT6 4BN</cmn:ZipCode>
                  </Address>
        </ContactDetails>
          <ProcessingDate>2014-08-19</ProcessingDate>
          <BankAccount><FirstLastVoucherCode>FirstLast</FirstLastVoucherCode><AgencyBankCode>0234</AgencyBankCode><SortCode>SC300037</SortCode><AccountNumber>46990760</AccountNumber><TotalVouchers>1</TotalVouchers></BankAccount>
               </Voucher>


          </DDIVouchers>
      </Document>
  </Data>
</VocaDocument>

so how should I write and call that function to pad each of my columns?

Our Man in Bananas
  • 5,809
  • 21
  • 91
  • 148

1 Answers1

1

You say this line causes a fail...

<xsl:value-of select="func:padStr('../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter', ' '), 1, 10)"/>

There are three reasons why..

  1. You have not declared the namespace func anywhere
  2. You are passing in the xpath expression as a literal string for the first argument, rather than just the expression itself
  3. You parentheses are mis-matched! 1 opening bracket, 2 closing....

It probably should look this this:

<xsl:value-of select="func:padStr(../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter, '          ', 10)"/>

(Assuming you declare the func namespace too...

<xsl:stylesheet version="2.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:func="myfunc">

Of course, having to pass in a string of 10 characters, is not convenient, so better still you could define a function like this...

<xsl:function name="func:padStr">
    <xsl:param name="str"/> 
    <xsl:param name="chr"/> 
    <xsl:param name="len"/> 
    <xsl:variable name="pad">
        <xsl:for-each select="1 to $len">
            <xsl:value-of select="$chr" />
        </xsl:for-each>
    </xsl:variable>
    <xsl:value-of select="substring(concat($str,$pad),1,$len)"/>
</xsl:function>

Then you could call it with just a single padding character

<xsl:value-of select="func:padStr(../../msg:StreamStart/msg:Stream/msg:AgencyBankParameter, ' ', 10)"/>
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Thanks but I get an error `"Namespace 'myfunc' does not contain any functions. Line: 47 Char: 2 Code: 0 URI: http://www.w3schools.com/xsl/tryxslt_result.asp "` – Our Man in Bananas Sep 02 '14 at 19:01
  • 1
    Don't use w3schools! That is only XSLT 1.0. Try it with http://xsltransform.net/ instead. It's much lovelier. – Tim C Sep 02 '14 at 19:03
  • 1
    MSXML6 only supports XSLT 1.0. You'd have to convert the function to a named-template if you did want to use XSLT 1.0. – Tim C Sep 02 '14 at 19:24
  • could you show me how to create a named template that would work please - or point me in the right direction? – Our Man in Bananas Sep 02 '14 at 19:26
  • I would point you in this direction... http://stackoverflow.com/questions/8858153/how-can-i-use-xslt-1-0-to-right-justify-plain-text-output – Tim C Sep 02 '14 at 19:37
  • 1
    You should probably ask a whole new question if you did want to use XSLT 1.0. This question is tagged XSLT 2.0, and the first sentence does say "I have to convert xml data into fixed length text file using XSLT 2.0". My answer would become irrelevant if you did change your question to XSLT 1.0 only. Thanks. – Tim C Sep 02 '14 at 19:45
  • yes you're right, I'll do that but leave this one open as your answer might help others in the future. – Our Man in Bananas Sep 02 '14 at 20:22