xslt-2.0xslt-3.0

XSLT to Split segment based on its Element occurrence


I am trying to write a XSLT which will create separate statement segments for each of Statement1/Cliam, Statement2/Cliam and Statement3/Cliam in a sequence. All statement1, statement2, statement3 segments we need to replace with Statement segment itself.

My xslt is returning an empty output or mostly the same input. Please have a look at it.

Input sample:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:CustomerMas xmlns:ns0="http://123.com">
    <Statement1>
        <CustomerAssi_S_IM action="123">
            <table>CustomerAss_S_IM</table>
            <Cliam>
                <Field1>CM</Field1>
            </Cliam>
            <Cliam>
                <Field1>33</Field1>
                <Field2>31</Field2>
            </Cliam>
        </CustomerAssi_S_IM>
    </Statement1>
    <Statement2>
        <CustomerAssi_P_IM action="456">
            <table>second</table>
            <Cliam>
                <Field1>D4</Field1>
            </Cliam>
            <Cliam>
                <Field1>34</Field1>
                <Field2>31</Field2>
            </Cliam>
        </CustomerAssi_P_IM>
    </Statement2>
    <Statement3>
        <CustomerAssi_Q_IM action="789">
            <table>second</table>
            <Cliam>
                <Field1>D4</Field1>
            </Cliam>        
        </CustomerAssi_Q_IM>
    </Statement3>
</ns0:CustomerMas>

Output sample:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:CustomerMas xmlns:ns0="http://123.com">
    <Statement>
        <CustomerAssi_S_IM action="123">
            <table>CustomerAss_S_IM</table>
            <Cliam>
                <Field1>CM</Field1>
            </Cliam>
        </CustomerAssi_S_IM>
    </Statement>
    <Statement>
        <CustomerAssi_S_IM action="123">
            <table>CustomerAss_S_IM</table>
            <Cliam>
                <Field1>33</Field1>
                <Field2>31</Field2>
            </Cliam>
        </CustomerAssi_S_IM>
    </Statement>
    <Statement>
        <CustomerAssi_P_IM action="456">
            <table>second</table>
            <Cliam>
                <Field1>D4</Field1>
            </Cliam>
        </CustomerAssi_P_IM>
    </Statement>
    <Statement>
        <CustomerAssi_P_IM action="456">
            <table>second</table>
            <Cliam>
                <Field1>34</Field1>
                <Field2>31</Field2>
            </Cliam>
        </CustomerAssi_P_IM>
    </Statement>
    <Statement>
        <CustomerAssi_Q_IM action="789">
            <table>second</table>
            <Cliam>
                <Field1>D4</Field1>
            </Cliam>
        </CustomerAssi_Q_IM>
    </Statement>
</ns0:CustomerMas>

This is the XSLT I have written so far:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://123.com">
    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Statement1">
        <Statement>
            <xsl:for-each select="ns0:CustomerAssi_S_IM/ns0:Cliam">
                <Statement>
                    <CustomerAssi_S_IM>
                        <xsl:copy-of select="../@*"/>
                        <xsl:copy-of select="../table"/>
                        <Cliam>
                            <xsl:copy-of select="."/>
                        </Cliam>
                    </CustomerAssi_S_IM>
                </Statement>
            </xsl:for-each>
        </Statement>
    </xsl:template>
    <xsl:template match="Statement2">
        <Statement>
            <xsl:for-each select="ns0:CustomerAssi_P_IM/ns0:Cliam">
                <Statement>
                    <CustomerAssi_P_IM>
                        <xsl:copy-of select="../@*"/>
                        <xsl:copy-of select="../table"/>
                        <Cliam>
                            <xsl:copy-of select="."/>
                        </Cliam>
                    </CustomerAssi_P_IM>
                </Statement>
            </xsl:for-each>
        </Statement>
    </xsl:template>
    <xsl:template match="Statement3">
        <Statement>
            <xsl:for-each select="ns0:CustomerAssi_Q_IM/ns0:Cliam">
                <Statement>
                    <CustomerAssi_Q_IM>
                        <xsl:copy-of select="../@*"/>
                        <xsl:copy-of select="../table"/>
                        <Cliam>
                            <xsl:copy-of select="."/>
                        </Cliam>
                    </CustomerAssi_Q_IM>
                </Statement>
            </xsl:for-each>
        </Statement>
    </xsl:template>
</xsl:stylesheet>

Solution

  • Your XPaths are not matching your input XML. Only the document element CustomerMas has the ns0 namespace prefix and is bound to the namespace http://123.com. The rest of the elements in that document are not bound to any namespace. So, you should not apply ns0 namespace prefix to your XPaths (unless your data actually does match and you posted a bad example XML doc).

    You can use a more generic XPath for the for loop and the Claim parent elements, and construct an element using the name() of the parent element for that Claim element.

    Inside of the for loop for Claim the <xsl:copy-of select="."/> will copy that element, so no need to wrap in another Claim.

    I would consolidate the logic for your template and change the template match to apply to all of the Statement elements:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
      <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="Statement1|Statement2|Statement3">
        <Statement>
            <xsl:for-each select="*/Cliam">
                <Statement>
                    <xsl:element name="{name(..)}">
                        <xsl:copy-of select="../@*"/>
                        <xsl:copy-of select="../table"/>
                        <xsl:copy-of select="."/>
                    </xsl:element>
                </Statement>
            </xsl:for-each>
        </Statement>
      </xsl:template>
    </xsl:stylesheet>