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>