Our Man in Bananas
Our Man in Bananas

Reputation: 5981

xslt to transform xml with global element at start

I have an xml file with the below structure:

<?xml version="1.0" encoding="UTF-8" ?> 
 <VocaDocument xmlns:cmn="http://www.voca.com/schemas/common" xmlns:me="http://www.voca.com/schemas/messaging">
  <Data>
      <Document>
          <StreamStart>
              <Stream>
                  <BankName>LLOYDS BANK PLC</BankName> 
                  <BankCode>0004</BankCode> 
                  <StreamCode>01</StreamCode> 
                  <VoucherSortCode>SC987654</VoucherSortCode> 
                  <VoucherAccountNumber>99775533</VoucherAccountNumber> 
              </Stream>
          </StreamStart>
          <DDIVouchers>
              <Voucher>
                  <TransactionCode>NEW</TransactionCode> 
                  <OriginatorIdentification>
                      <ServiceUserName>A CUSTOMER</ServiceUserName> 
                      <ServiceUserNumber>123456</ServiceUserNumber> 
                  </OriginatorIdentification>
                  <PayingBankAccount>
                      <BankName>A BANK LTD</BankName> 
                      <AccountName>A PERSON</AccountName> 
                      <AccountNumber>99887766</AccountNumber> 
                      <UkSortCode>SC123456</UkSortCode> 
                  </PayingBankAccount>
                  <ReferenceNumber>BACS1234567</ReferenceNumber> 
                  <ContactDetails>
                      <PhoneNumber>020 83395862</PhoneNumber> 
                      <FaxNumber /> 
                      <Address>
                          <cmn:AddresseeName>A NAME</cmn:AddresseeName> 
                          <cmn:PostalName>A CUSTOMER</cmn:PostalName> 
                          <cmn:AddressLine>Line 1</cmn:AddressLine> 
                          <cmn:TownName>Town</cmn:TownName> 
                          <cmn:CountyIdentification /> 
                          <cmn:CountryName>UNITED KINGDOM</cmn:CountryName> 
                          <cmn:ZipCode>AA1 4AB</cmn:ZipCode> 
                      </Address>
                  </ContactDetails>
                  <ProcessingDate>2014-08-19</ProcessingDate> 
                  <BankAccount>
                      <FirstLastVoucherCode>FirstLast</FirstLastVoucherCode> 
                      <AgencyBankCode>0123</AgencyBankCode> 
                      <SortCode>SC654321</SortCode> 
                      <AccountNumber>12345678</AccountNumber> 
                      <TotalVouchers>1</TotalVouchers> 
                      <Counter /> 
                  </BankAccount>
              </Voucher>

what I need to do is output the data in fixed length text format, one line for each of the Voucher elements with the Stream elements at the start of each line.

so it should look something like the below in the output file (that will be loaded up into the AS400 accounting system):

LLOYDS BANK PLC                                                       00040001SC9876549977553300000000NEW       A CUSTOMER                           123456A BANK                                                                            A PERSON ...

So, my question is: how could I match each Voucher record and put the Stream record at the start of each record - do I need to use a variable to store the values in the Stream element?

Once I have done that I can look into using concat with substring to maybe pad leading spaces into the output as I have see in SO 16816251: xml-to-fixed-width-text-file-with-xsl-style-sheet

Thanks for any suggestions or tips

Upvotes: 0

Views: 131

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116982

First thing, let us take a minimal, but complete example of XML input:

<VocaDocument>
  <Data>
      <Document>
          <StreamStart>
              <Stream>
                  <BankName>LLOYDS BANK PLC</BankName> 
              </Stream>
          </StreamStart>
          <DDIVouchers>
              <Voucher>
                  <TransactionCode>NEW</TransactionCode> 
                  <OriginatorIdentification>
                      <ServiceUserNumber>123456</ServiceUserNumber> 
                  </OriginatorIdentification>
               </Voucher>              
               <Voucher>
                  <TransactionCode>OLD</TransactionCode> 
                  <OriginatorIdentification>
                      <ServiceUserNumber>789012</ServiceUserNumber> 
                  </OriginatorIdentification>
               </Voucher>
          </DDIVouchers>
      </Document>
  </Data>
</VocaDocument>

Next, consider the following stylesheet:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />

<xsl:template match="/">
    <xsl:for-each select="VocaDocument/Data/Document/DDIVouchers/Voucher">
        <xsl:value-of select="../../StreamStart/Stream/BankName" />
        <xsl:value-of select="TransactionCode" />
        <xsl:value-of select="OriginatorIdentification/ServiceUserNumber" />
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template> 

</xsl:stylesheet>

The result here will be:

LLOYDS BANK PLCNEW123456
LLOYDS BANK PLCOLD789012

Explanation:

The instruction:

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

puts us in the context of Voucher. 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.


Alternatively, you could do:

<xsl:template match="/">
    <xsl:variable name="BankName" select="VocaDocument/Data/Document/StreamStart/Stream/BankName" />
    <xsl:for-each select="VocaDocument/Data/Document/DDIVouchers/Voucher">
        <xsl:value-of select="$BankName" />
        <xsl:value-of select="TransactionCode" />
        <xsl:value-of select="OriginatorIdentification/ServiceUserNumber" />
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template> 

The problem of padding the individual values is left as an exercise for the reader (who has already found the solution).

Upvotes: 2

Related Questions