1

I have this source XML

<?xml version="1.0" encoding="UTF-8" ?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <StartOfTransmission>
        <SenderId>SENDERSID</SenderId>
        <ReceiverId>RECEIVERSID</ReceiverId>
        <TRANSDATE>13-09-2010</TRANSDATE>
        <TRANSTIME>12:00:00</TRANSTIME>
        <SenderReference>1234</SenderReference>
        <ReceiverReference/>
        <ApplicationReference>ORDHDR</ApplicationReference>
    </StartOfTransmission>
    <FileDetails>
        <GenerationNumber>12344</GenerationNumber>
        <VersionNumber>0001</VersionNumber>
        <FileCreationDate>25-04-2008</FileCreationDate>
    </FileDetails>
    <Order>
        <OrderHeader>
            <OrderDate>13-09-2010</OrderDate>
            <OrderTime>16:00:00</OrderTime>
            <OrderReference>Order Ref1</OrderReference>
            <RetailerId>132</RetailerId>
            <LocationDetails>
                <ANACode>5013546167507</ANACode>
                <CustomerLocationCode>16750</CustomerLocationCode>
            </LocationDetails>
        </OrderHeader>
    </Order>
</Document>

and I want it to use a version 2 style sheet to convert into this format

<?xml version="1.0" encoding="UTF-8"?>
<a:Document xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://127.0.0.1:8080 http://127.0.0.1:8080/tbattributesxml2.xsd"
          xmlns:a="http://127.0.0.1:8080">
    
    <a:StartOfTransmission SenderId="SENDERSID" ReceiverId="RECEIVERSID" transdatetime="2010-09-13T12:00:00.000" SenderReference="1234" ReceiverReference="" ApplicationReference="ORDHDR"/>
    <a:FileDetails GenerationNumber="12344" VersionNumber="0001" FileCreationDate="2008-04-25T00:00:00.000"/>
    <a:Order>
        <a:OrderHeader orderdate="2010-09-13T16:00:00.000" OrderReference="Order Ref1" RetailerId="132" DeliveryType="CARR" BackOrderFlag="N" OrderType="B" InternetOrderReference="Internet Ref">
            <a:LocationDetails ANACode="5013546167507" CustomerLocationCode="16750"/>
        </a:OrderHeader>
    </a:Order>
</a:Document>
 

I currently have this stylesheet but for some reason the LocationDetails element is getting formatted as a single block attribute and not its own element called LocationDetails with its own set of attributes. I have tried to follow the formatting set for the previous elements which works for those but i've hit a wall. As always, any help gratefully received.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:a="http://127.0.0.1:8080"
    exclude-result-prefixes="xs"
    version="2.0" >
    
    <xsl:output method="xml" indent="no" />
    <xsl:strip-space elements="*"/>
       
    <xsl:template match="Document" >
        <xsl:text>&#xA;</xsl:text>
        <a:Document xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://127.0.0.1:8080 http://127.0.0.1:8080/test/tbattributesxml2.xsd">
            <xsl:apply-templates/>
        </a:Document>
    </xsl:template>
    
    <xsl:template match="StartOfTransmission">
        <xsl:text>&#xA;&#x9;</xsl:text>
        <a:StartOfTransmission transdatetime="{replace(TRANSDATE, '(.{2})-(.{2})-(.{4})', '$3-$2-$1')}T{TRANSTIME}">
            <xsl:apply-templates select="* except(TRANSDATE, TRANSTIME)"/>
        </a:StartOfTransmission>
    </xsl:template> 
    
    <xsl:template match="StartOfTransmission/*">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>  
    
    <xsl:template match="FileDetails">
        <xsl:text>&#xA;&#x9;</xsl:text>
        <xsl:variable name="dte" as="xs:string">
            <xsl:value-of select="concat(substring(FileCreationDate, 7, 4), '-', substring(FileCreationDate, 4, 2), '-', substring(FileCreationDate, 1, 2))"/>
        </xsl:variable>
        <xsl:variable name="dateTime" select="concat($dte,'T','00:00:00.000')"/>
        <a:FileDetails FileCreationDate="{$dateTime}">
            <xsl:apply-templates select="* except(FileCreationDate)"/>
        </a:FileDetails>      
    </xsl:template>
    
    <xsl:template match="FileDetails/*">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>  
    
    <xsl:template match="Order">
        <xsl:text>&#xA;&#x9;</xsl:text>
        <a:Order>
            <xsl:apply-templates select="*" />
            <xsl:text>&#xA;&#x9;</xsl:text>
        </a:Order>
    </xsl:template>
    

    <xsl:template match="OrderHeader">
        <xsl:text>&#xA;&#x9;&#x9;</xsl:text>       
        <a:OrderHeader >
            <xsl:apply-templates select="*"/>
        </a:OrderHeader>
    </xsl:template>
    
    <xsl:template match="OrderHeader/*">
        <xsl:attribute name="{name()}" select="."/>
        
    </xsl:template>  
    
    <xsl:template match="LocationDetails">
        <xsl:text>&#xA;&#x9;&#x9;&#x9;</xsl:text>
        <a:LocationDetails>
            <xsl:apply-templates select="ANACode"/>
        </a:LocationDetails> 
        
    </xsl:template>
    
    <xsl:template match="LocationDetails/*">
        <xsl:attribute name="{name()}" select="*"/>
    </xsl:template> 
    
</xsl:stylesheet>

This is the incorrect output, the missing orderdate/ordertime to orderdate conversion in the OrderHeader element can be ignored in any answer, its the locationdetails that is the problem.

<?xml version="1.0" encoding="UTF-8"?>
<a:Document xmlns:a="http://127.0.0.1:8080" xmlns:saxon="http://saxon.sf.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://127.0.0.1:8080 http://127.0.0.1:8080/test/tbattributesxml2.xsd">
    <a:StartOfTransmission transdatetime="2010-09-13T12:00:00" SenderId="SENDERSID" ReceiverId="RECEIVERSID" SenderReference="1234" ReceiverReference="" ApplicationReference="ORDHDR"/>
    <a:FileDetails FileCreationDate="2008-04-25T00:00:00.000" GenerationNumber="12344" VersionNumber="0001"/>
    <a:Order>
        <a:OrderHeader OrderDate="13-09-2010" OrderTime="16:00:00" OrderReference="Order Ref1" RetailerId="132" LocationDetails="501354616750716750"/>
    </a:Order></a:Document>

2 Answers 2

1

Option 1

Give LocationDetails template a priority

  <xsl:template match="LocationDetails" priority="2">
    <xsl:text>&#xA;&#x9;&#x9;&#x9;</xsl:text>
    <a:LocationDetails>
      <xsl:apply-templates select="ANACode"/>
    </a:LocationDetails> 
  </xsl:template>

Option 2:

Change this template

  <xsl:template match="OrderHeader/*">
    <xsl:attribute name="{name()}" select="."/>
  </xsl:template>  

to this:

  <xsl:template match="OrderHeader/*[not(self::LocationDetails)]">
    <xsl:attribute name="{name()}" select="."/>
  </xsl:template>  
Sign up to request clarification or add additional context in comments.

3 Comments

Yes raising the template's priority is the key to solving this question! NB also changing the match attribute of the LocationDetails template from LocationDetails to OrderHeader/LocationDetails is a third way to elevate its priority above the more generic OrderHeader/* template. The rules for template priorities are defined here: w3.org/TR/xslt20/#conflict
Hi, adding priority="2" results in this the attributes being blank populated <a:LocationDetails ANACode="" CustomerLocationCode=""/></a:OrderHeader>
also needed to change the * to "." in here <xsl:template match="LocationDetails/*"> <xsl:attribute name="{name()}" select="*"/> </xsl:template>
0

Not really an answer, just a general note regarding your approach. AFAICT, the bulk of the required transformation could be carried out using only two generic templates:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://127.0.0.1:8080">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*[*]">
    <xsl:element name="a:{name()}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="*">
    <xsl:attribute name="{name()}" select="."/>
</xsl:template>

</xsl:stylesheet>

Applied to your example input, this will return:

Result

<?xml version="1.0" encoding="UTF-8"?>
<a:Document xmlns:a="http://127.0.0.1:8080">
   <a:StartOfTransmission SenderId="SENDERSID"
                          ReceiverId="RECEIVERSID"
                          TRANSDATE="13-09-2010"
                          TRANSTIME="12:00:00"
                          SenderReference="1234"
                          ReceiverReference=""
                          ApplicationReference="ORDHDR"/>
   <a:FileDetails GenerationNumber="12344"
                  VersionNumber="0001"
                  FileCreationDate="25-04-2008"/>
   <a:Order>
      <a:OrderHeader OrderDate="13-09-2010"
                     OrderTime="16:00:00"
                     OrderReference="Order Ref1"
                     RetailerId="132">
         <a:LocationDetails ANACode="5013546167507" CustomerLocationCode="16750"/>
      </a:OrderHeader>
   </a:Order>
</a:Document>

Now you just need to add a few more specific templates to handle the exceptions - for example:

<xsl:template match="TRANSDATE">
    <xsl:attribute name="transdatetime" select="concat(replace(., '(.{2})-(.{2})-(.{4})', '$3-$2-$1T'), ../TRANSTIME)"/>
</xsl:template>

<xsl:template match="TRANSTIME"/>

In addition, I cannot see the sense in specifying indent="no" and then adding your own indentation using xsl:text.

1 Comment

i'll take out the indentation at the end. its visually easier for me to compare the generated output with the intended output if the indentations are all the same.

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.