Home > Blockchain >  Copying the parents nodes upto the root to create multiple items
Copying the parents nodes upto the root to create multiple items

Time:02-24

I am new to XSLT and I am working on a tricky xml transformation requirement. My source xml is as mentioned below.

<Level_0>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>1</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>2</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>3</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
</Level_0>

Requirement - I need to have each item having its parents up to the level_1, means output should be as mentioned below. Basically, everything should be repeated for each Item from level_1.

<Level_0>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>1</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>2</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>3</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
</Level_0>


XSLT I tried. But I am getting weird output.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes"/>


    <xsl:template match="SalesInvoice">
        <NewInvoice>
            <xsl:copy-of select="../../../../Level_1/descendant::*[not(name()='SalesInvoice' or ancestor::SalesInvoice)]"/>
            <xsl:copy-of select="."/>
        </NewInvoice>
    </xsl:template>
</xsl:stylesheet>

CodePudding user response:

This is one way to approach it. For each of the SalesInvoice process, apply-templates starting from it's Level_1, but pass through the SalesInvoice as a param (I used @tunnel, but you could explicitly define the param and pass it through at each level in each template). In the template matching SalesInvoice only copy the content if it's the $invoice-context, otherwise it gets dropped.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Level_0">
        <xsl:copy>
            <xsl:for-each select="//SalesInvoice">
                <xsl:apply-templates select="ancestor::Level_1" mode="repeat">
                    <xsl:with-param name="invoice-context" select="." tunnel="yes"/>
                </xsl:apply-templates>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*" mode="repeat">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="repeat"/>    
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="SalesInvoice" mode="repeat">
        <xsl:param name="invoice-context" tunnel="yes"/>
        <xsl:if test="generate-id(.) eq generate-id($invoice-context)">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>
  • Related