Skip to content Skip to sidebar Skip to footer

XSLT: Transform Nodes By Comparing Their Child Nodes

To start off, I'm aware of this SO question which is a bit different. I have an XML file which looks like this:

Solution 1:

I hesitate whether to post this at all, because you seem to be a beginner at XSLT and this is a complex solution to a complex problem. It may take you quite some time to wrap your head around this.

How this works:

In the first pass, we generate a pmt element for each PmtInf element in your input XML, and populate it with a string that contains a name/value pair for each node (element or attribute) descendant of the current PmtInf. In the given example, each such pmt element would look similar to:

<pmt id="idp1696">[PmtInfId:asd][PmtMtd:TRF][BtchBookg:false][PmtTpInf:NORMtest][InstrPrty:NORM][SvcLvl:test][Prtry:test][ReqdExctnDt:date][Dbtr:somethingaddr 1][Nm:something][PstlAdr:addr 1][AdrLine:addr 1]</pmt>

In the next step we apply Muenchian grouping to the pmt nodes generated in the first pass. For each distinct pmt node, we create a PmtInf element and populate it with:

  1. the contents of the corresponding PmtInf element, except CdtTrfTxInf ;

  2. a copy of all CdtTrfTxInf elements from all the members of the group.

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="ns0"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="pmt" match="ns0:pmt" use="." />
<xsl:key name="PmtInf" match="ns0:PmtInf" use="generate-id()" />

<xsl:variable name="input" select="/" />

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="ns0:CstmrCdtTrfInitn">
    <!-- first pass -->
    <xsl:variable name="first-pass-rtf">
        <xsl:apply-templates select="ns0:PmtInf" mode="gen-key"/>
    </xsl:variable>
    <xsl:variable name="first-pass" select="exsl:node-set($first-pass-rtf)" />
    <!-- output -->
    <xsl:copy>
        <xsl:copy-of select="ns0:GrpHdr"/>
            <!-- for each distinct pmt -->
            <xsl:for-each select="$first-pass/ns0:pmt[count(. | key('pmt', .)[1]) = 1]">
                <xsl:variable name="id" select="@id" />
                <xsl:variable name="ids" select="key('pmt', .)/@id" />
                <PmtInf>
                    <!-- switch context back to XML input -->
                    <xsl:for-each select="$input">
                        <xsl:copy-of select="key('PmtInf', $id)/*[not(self::ns0:CdtTrfTxInf)]"/>
                        <xsl:copy-of select="key('PmtInf', $ids)/ns0:CdtTrfTxInf"/>
                    </xsl:for-each>
                </PmtInf>
            </xsl:for-each>
    </xsl:copy>
</xsl:template>

<xsl:template match="ns0:PmtInf" mode="gen-key">
    <pmt id="{generate-id()}">
        <xsl:apply-templates select="@*|*" mode="gen-key"/>
    </pmt>
</xsl:template>

<xsl:template match="@*|node()" mode="gen-key">
    <xsl:text>[</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>:</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>]</xsl:text>
    <xsl:apply-templates select="@*|*" mode="gen-key"/>
</xsl:template>

<xsl:template match="ns0:CdtTrfTxInf" mode="gen-key"/>

</xsl:stylesheet>

Post a Comment for "XSLT: Transform Nodes By Comparing Their Child Nodes"