Home > Back-end >  XSLT 2.0 Transformations - is this the right approach?
XSLT 2.0 Transformations - is this the right approach?

Time:04-27

I have the following XML payload :

    <?xml version="1.0" encoding="utf8" ?>
    <Output>
    <Error>
        <Status>0</Status>
        <Details>No errors</Details>
    </Error>
    <Synopsis>
        <Count>451</Count>
    </Synopsis>
    <BankAccounts>
        <BankAccount AcctNo="103" CustName="Frank" BalanceAmount="" Inactive="N" NoOfAccounts="1" >
            <Addresses>
                <Address>ABC</Address>
                <Address>XYZ</Address>
            </Addresses>
        </BankAccount>
        <BankAccount AcctNo="101" CustName="Jane" BalanceAmount="10005" Inactive="N" NoOfAccounts="1" >
            <Addresses>
                <Address>LMN</Address>
                <Address>QWE</Address>
            </Addresses>
        </BankAccount>
        
    </BankAccounts>
</Output>

I need to transform it into the following :

<?xml version="1.0" encoding="UTF-8"?>
<Output>
    <Count>451</Count>
    <?xml-multiple bankAccounts?>
    <bankAccounts>
        <acctNo>103</acctNo>
        <custName>Frank</custName>
        <balanceAmount/>
        <inactive>N</inactive>
        <noOfAccounts>1</noOfAccounts>
        <?xml-multiple Address?>
        <Address>ABC</Address>
        <Address>XYZ</Address>      
    </bankAccounts>
    <bankAccounts>
        <acctNo>101</acctNo>
        <custName>Jane</custName>
        <balanceAmount>10005</balanceAmount>
        <inactive>N</inactive>
        <noOfAccounts>1</noOfAccounts>
        <?xml-multiple Address?>
        <Address>LMN</Address>
        <Address>QWE</Address>
    </bankAccounts> 
</Output>

Am an amateur w.r.t XSLT and by looking up SO and asking questions here have somehow been able to come up with an XSLT as below .

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <!-- Identity template, copies everything as is -->
    <xsl:template match="@*|node()"> 
        <xsl:copy> 
            <xsl:apply-templates select="@*|node()"/> 
        </xsl:copy> 
    </xsl:template> 
    
    <!-- breakdown attributes of BankAccount into child elements -->
    <xsl:template match="BankAccount/@*">
        <xsl:element name="{lower-case(substring(name(), 1, 1))}{substring(name(), 2)}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
    
    
    
    <!--  Drop 'Synopsis' element NOT children AND insert a PI at the end of this operation -->
    <xsl:template match="Synopsis" >
        <xsl:apply-templates/>
        <xsl:processing-instruction name="xml-multiple">
            bankAccounts
        </xsl:processing-instruction>
        
    </xsl:template>
    
    <!--  This will drop 'Result/Header' element and its children  -->
    <xsl:template match="Output/Error" />   
    
    <!--  This will drop 'BankAccounts' element BUT preserve its children -->
    <xsl:template match="BankAccounts">
        <xsl:apply-templates/>
    </xsl:template>
    
    <!-- rename element 'BankAccount' to 'bankAccounts' -->
    <xsl:template match="BankAccount">
        <bankAccounts>
            <xsl:apply-templates select="@* | node()" />
        </bankAccounts>
    </xsl:template>
    
    <!--  Drop 'Addresses' element and insert PI first inside 'Addresses' -->
    <xsl:template match="Addresses" >
        <xsl:processing-instruction name="xml-multiple">
            Address
        </xsl:processing-instruction>
        <xsl:apply-templates/>
        
        
    </xsl:template>
</xsl:stylesheet>

This does seem to work but here are my queries:

#1 Is there anything that is wrong / non performant in the above XSL ? something that should be avoided ?

#2 why is the identity template required ? I tried removing it but it seems to mess up the o/p ?

#3 is each instruction scanning through the entire XML document ? am wondering if there is a way to 'chain' where o/p of one XSLT operation can be fed to the next xslt operation ?

#4 while breaking down attributes of 'BankAccount' element into child elements have used a function copy pasted from another question but I dont really understand why substring is required ? Am guessing it is trying to only process the first alphabet

Thanks

CodePudding user response:

#1 Is there anything that is wrong / non performant in the above XSL ? something that should be avoided ?

The main thing I notice is that you can't rely on the order of attributes: the elements in your output might end up in a different order, it's implementation-dependent.

#2 why is the identity template required ? I tried removing it but it seems to mess up the o/p ?

You're calling apply-templates to process some nodes (for example Address elements, and whitespace text nodes) for which you don't have an explicit template rule. Your fallback template rule is different from the default (built-in) fallback rule.

#3a is each instruction scanning through the entire XML document ?

Each apply-templates instruction is selecting a set of nodes to be processed (by default, the children of the current node), and conceptually, all the template rules in the stylesheet are examined to see which of them is the best fit for processing each of these nodes. (In practice, the XSLT processor may have a better strategy).

#3b am wondering if there is a way to 'chain' where o/p of one XSLT operation can be fed to the next xslt operation ?

Yes, but that's a whole new subject. Search for XSLT pipelines.

#4 while breaking down attributes of 'BankAccount' element into child elements have used a function copy pasted from another question but I dont really understand why substring is required ? Am guessing it is trying to only process the first alphabet

You're converting the first character of the name to lower-case and copying the rest of the string unchanged.

  • Related