Ordering nodes in XSL Transformation - xslt

Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData version="2.1" xmlns="bank.xsd">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BANK" distName="CITY-1/ABC-1/BANK-1" operation="create" timeStamp="2013-07-27T15:48:20"/>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="update" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/BANK-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/BANK-1/Branch-1/BranchItem-2" operation="update" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Transformation XSL :
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:x="bank.xsd" exclude-result-prefixes="x">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:object[#class = 'BANK' ]">
</xsl:template>
<xsl:template match="x:object[#class = 'Branch' ]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItem' ]">
<xsl:variable name="branchItem" select="."/>
<xsl:choose>
<xsl:when test="$branchItem/#operation='update' and not(contains($branchItem/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="x:object[#class = 'BranchItemPeriod' ]">
<xsl:variable name="branchPeiod" select="."/>
<xsl:choose>
<xsl:when test="$branchPeiod/#operation='update' and not(contains($branchPeiod/#distName, 'JOBS_CREATED_USING_NE_LOCAL_UI'))">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="distName">
<xsl:value-of select="concat(substring-before( #distName,'BANK-1/' ), substring-after( #distName, 'BANK-1/'))"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output XML :
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
Desired OUTPUT XML:
<?xml version="1.0" encoding="UTF-8"?>
<BigData xmlns="bank.xsd" version="2.1">
<InsideData type="plan" name="testBANK" id="10">
<header>
<log dateTime="2013-07-27T15:52:30"/>
</header>
<object class="Branch" distName="CITY-1/ABC-1/Branch-1" operation="create" timeStamp="2013-07-27T15:48:31"/>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-1/BranchItemPeriod-1" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="delete" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItem" distName="CITY-1/ABC-1/Branch-1/BranchItem-2" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="BranchItemPeriod" distName="CITY-1/ABC-1/Branch-1/BranchItem-2/BranchItemPeriod-2" operation="create" timeStamp="2013-07-27T15:51:25">
<p name="Week">0</p>
<p name="interval">10</p>
</object>
<object class="Sleep" distName="CITY-1/ABC-1/Sleep-1" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
<object class="Dance" distName="CITY-1/ABC-1/Dance-5" operation="create" timeStamp="2013-07-27T15:50:42">
<p name="openDate">2013-07-27</p>
<p name="closeDate">2013-07-29</p>
</object>
</InsideData>
</BigData>
I could achieve most of desired output except for few...
I want the output to be sorted based on distName attribute of object nodes.
I want the sort to happen only to certain child nodes with class names as Branch , BranchItem , BranchItemPeriod.
Here i try for update with delete and create operation, so i want also to maintain the order of delete and create too which i do in present transformation logic or else can it so happen that i sort all first based on above criteria and apply the other transformation logic.
Any suggestion or help is highly appreciated.

I think what you need here is a template that matches the InsideData element, where you can then select the child object elements in the order you require.
You would first start by outputting the non-"object" elements, assuming these always come before the object elements.
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
Then you would select the object elements with the class attribute you desire, sorting in the order you require too:
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
Finally, you would output the object elements which have the the class attributes.
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
Try adding this template to your XSLT to see how you get on:
<xsl:template match="x:InsideData">
<xsl:copy>
<xsl:apply-templates select="#*|node()[not(self::x:object)]"/>
<xsl:apply-templates select="x:object[#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod']">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
<xsl:apply-templates select="x:object[not(#class='Branch' or #class='BranchItem' or #class='BranchItemPeriod')]"/>
</xsl:copy>
</xsl:template>

Related

Grouping the consecutive elements in XSLT

I am new to XSLT and XML. I have the following XML. I wanted to group the consecutive child elements.
<Root>
<Child No="1" Month="0" Date="13/08/2014" Payment="100">
<Totals/>
</Child>
<Child No="2" Month="1" Date="13/09/2014" Payment="100">
<Totals/>
</Child>
<Child No="3" Month="2" Date="13/10/2014" Payment="200">
<Totals/>
</Child>
<Child No="4" Month="3" Date="13/11/2014" Payment="300">
<Totals/>
</Child>
<Child No="5" Month="4" Date="13/12/2014" Payment="300">
<Totals/>
</Child>
<Child No="6" Month="5" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="7" Month="6" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="8" Month="7" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="9" Month="8" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
<Child No="10" Month="9" Date="13/01/2015" Payment="100">
<Totals/>
</Child>
</Root>
I need to create a new PP node when Child[n]/Payment <> Child[n-1]/Payment using the XSLT.
And I am expecting the below result,
<PPS>
<PP>
<Ps>
<StartMonth>0</StartMonth>
<EndMonth>1</EndMonth>
<P>
<Amount>100</Amount>
<startP>1</startP>
</P>
<P>
<Amount>100</Amount>
<startP>2</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>2</StartMonth>
<EndMonth>2</EndMonth>
<P>
<Amount>200</Amount>
<startP>3</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>3</StartMonth>
<EndMonth>4</EndMonth>
<P>
<Amount>300</Amount>
<startP>4</startP>
</P>
<P>
<Amount>300</Amount>
<startP>5</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>5</StartMonth>
<EndMonth>9</EndMonth>
<P>
<Amount>100</Amount>
<startP>6</startP>
</P>
<P>
<Amount>100</Amount>
<startP>7</startP>
</P>
<P>
<Amount>100</Amount>
<startP>8</startP>
</P>
<P>
<Amount>100</Amount>
<startP>9</startP>
</P>
<P>
<Amount>100</Amount>
<startP>10</startP>
</P>
</Ps>
</PP>
</PPS>
Here is my sample xslt
<xsl:stylesheet version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="x" match="Child" use="#Payment"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="CF">
<xsl:if test="generate-id(.) = generate-id(key('x',#Payment)[1])">
<PP>
<Ps>
<xsl:for-each select="key('x', #Payment)">
<P>
<Amount><xsl:value-of select="format-number(translate(#Payment, ',','.'),'0.00')"/></Amount>
<startP><xsl:value-of select="#Date"/></startP>
</P>
</xsl:for-each>
</Ps>
</PP>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
</xsl:stylesheet>
This would work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="Child">
<xsl:variable name="Payment" select="#Payment"/>
<xsl:if test="not(preceding::*[1][self::Child and #Payment = $Payment])">
<PPS>
<Ps>
<xsl:apply-templates select="current()"/>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</Ps>
</PPS>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
<xsl:template match="Child">
<xsl:param name="Payment"/>
<P>
<Amount>
<xsl:value-of select="#Payment"/>
</Amount>
<startP>
<xsl:value-of select="#No"/>
</startP>
</P>
<xsl:apply-templates select="following::*[1][self::Child and #Payment = $Payment]"/>
</xsl:template>
</xsl:stylesheet>

Grouping the immediate nodes in XSLT

I am using the XSLT 1.0 version and I have the below XML to transform,
<Root>
<Child No="1" Month="0" Date="13/08/2014" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="2" Month="1" Date="13/09/2014" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="3" Month="2" Date="13/10/2014" Payment="200">
<Totals amount="200"/>
</Child>
<Child No="4" Month="3" Date="13/11/2014" Payment="300">
<Totals amount="300"/>
</Child>
<Child No="5" Month="4" Date="13/12/2014" Payment="300">
<Totals amount="300"/>
</Child>
<Child No="6" Month="5" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="7" Month="6" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="8" Month="7" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="9" Month="8" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
<Child No="10" Month="9" Date="13/01/2015" Payment="100">
<Totals amount="100"/>
</Child>
</Root>
I wanted to transform The XML as
<PPS>
<PP>
<Ps>
<StartMonth>0</StartMonth>
<EndMonth>1</EndMonth>
<P>
<Amount>100</Amount>
<startP>1</startP>
</P>
<P>
<Amount>100</Amount>
<startP>2</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>2</StartMonth>
<EndMonth>2</EndMonth>
<P>
<Amount>200</Amount>
<startP>3</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>3</StartMonth>
<EndMonth>4</EndMonth>
<P>
<Amount>300</Amount>
<startP>4</startP>
</P>
<P>
<Amount>300</Amount>
<startP>5</startP>
</P>
</Ps>
</PP>
<PP>
<Ps>
<StartMonth>5</StartMonth>
<EndMonth>9</EndMonth>
<P>
<Amount>100</Amount>
<startP>6</startP>
</P>
<P>
<Amount>100</Amount>
<startP>7</startP>
</P>
<P>
<Amount>100</Amount>
<startP>8</startP>
</P>
<P>
<Amount>100</Amount>
<startP>9</startP>
</P>
<P>
<Amount>100</Amount>
<startP>10</startP>
</P>
</Ps>
</PP>
</PPS>
every time I wanted to create a "P" differing from the last Payment.
Here is the pseudo code, create a new PP, when child[n]/Payment <> child[n-1]/Payment.
P.S., I do not want to consider the Totals node even it has some values.
Many Thanks in advance.
You can use the following XSLT. It wouldn't have been so long if it was in XSLT2.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Root">
<PPS>
<xsl:for-each select="Child">
<xsl:variable name="Payment" select="#Payment"/>
<xsl:variable name="start-end">
<StartMonth>
<xsl:value-of select="#Month"/>
</StartMonth>
<xsl:choose>
<xsl:when test="following-sibling::*[1][self::Child and #Payment = $Payment]">
<EndMonth>
<xsl:apply-templates select="following-sibling::*[1]" mode="month">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</EndMonth>
</xsl:when>
<xsl:otherwise>
<EndMonth>
<xsl:value-of select="#Month"/>
</EndMonth>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="not(preceding-sibling::*[1][self::Child and #Payment = $Payment])">
<PP>
<Ps>
<xsl:copy-of select="$start-end"/>
<xsl:apply-templates select="current()"/>
<xsl:apply-templates select="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</Ps>
</PP>
</xsl:if>
</xsl:for-each>
</PPS>
</xsl:template>
<xsl:template match="Child">
<xsl:param name="Payment"/>
<P>
<Amount>
<xsl:value-of select="#Payment"/>
</Amount>
<startP>
<xsl:value-of select="#No"/>
</startP>
</P>
<xsl:apply-templates select="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Child" mode="month">
<xsl:param name="Payment"/>
<xsl:choose>
<xsl:when test="following-sibling::*[1][self::Child and #Payment = $Payment]">
<xsl:apply-templates select="following-sibling::*[1]" mode="month">
<xsl:with-param name="Payment" select="$Payment"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#Month"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Order of nodes in XSL Transformation

I have a xml which looks like below :
<?xml version="1.0" encoding="UTF-8"?>
<Object>
<Data type="plan" name="testAom" id="10">
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-1" operation="create" >
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-1/E-1" operation="create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="update">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="update">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
</Data>
</Object>
Here the order of the input source file is not guaranteed. But in the output after an XSL transformation, i require the output to be in certain order.
Below is the XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="obj[#class = 'D' ]">
<xsl:variable name="item" select="."/>
<xsl:choose>
<xsl:when test="$item/#operation='update'">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">NEW_create</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="obj[#class = 'E' ]">
<xsl:variable name="childitem" select="."/>
<xsl:choose>
<xsl:when test="$childitem/#operation='update'">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="operation">NEW_create</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The output i get is :
<?xml version="1.0" encoding="UTF-8"?>
<Object>
<Data type="plan" name="testAom" id="10">
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-1" operation="NEW_create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-1/E-1" operation="NEW_create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="delete">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="delete">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
</Data>
</Object>
Here when the operation attribute is "update", i need to do a delete and create.
The nodes above have a parent child relationship based on attribute distName, ie A-1/B-1/C-1/D-1 is parent of A-1/B-1/C-1/D-1/E-1.
For the update operation to work correct, i need the parent create and delete first and then followed by child delete and create always.
How can this be achieved irrespective of the order of input source xml file??
ie. obj nodes with class attribute D should be first in order than E.
Expected Output XML:
<?xml version="1.0"?>
<Object>
<Data type="plan" name="testAom" id="10">
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-1" operation="NEW_create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-1/E-1" operation="NEW_create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="delete">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj><obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="delete">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj><obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
</Data>
</Object>
I may be missing something in your requirements, but if not, then an update is simply a delete and a create and can be done in a single template. I focused my solution on the action, not on the class. I think my stylesheet produces exactly what you asked to be produced, but it may not be doing what you want. I hope it helps.
t:\ftemp>type objects.xml
<?xml version="1.0" encoding="UTF-8"?>
<Object>
<Data type="plan" name="testAom" id="10">
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-1" operation="create" >
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-1/E-1" operation="create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="update">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="update">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
</Data>
</Object>
t:\ftemp>call xslt objects.xml objects.xsl
<?xml version="1.0" encoding="utf-8"?>
<Object>
<Data type="plan" name="testAom" id="10">
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-1" operation="New_create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-1/E-1" operation="New_create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="delete">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="D" version="1.0" distName="A-1/B-1/C-1/D-2" operation="create">
<p name="Active">1</p>
<p name="Type">CPU</p>
<p name="StDate">2013-07-27T00:00:00+00:00</p>
<p name="StpDate">2013-07-29T00:00:00+00:00</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="delete">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
<obj class="E" version="1.0" distName="A-1/B-1/C-1/D-2/E-1" operation="create">
<p name="dayOfWeek">0</p>
<p name="interval">10</p>
</obj>
</Data>
</Object>
t:\ftemp>type objects.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="Object/Data">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<!--results are in distName order-->
<xsl:apply-templates select="obj">
<xsl:sort select="#distName"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!--for creation, only the attribute changes, nothing else-->
<xsl:template match="obj[#operation='create']">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="operation">New_create</xsl:attribute>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<!--remove those marked for deletion-->
<xsl:template match="obj[#operation='delete']"/>
<!--remove and recreate those marked for update-->
<xsl:template match="obj[#operation='update']">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="operation">delete</xsl:attribute>
<xsl:apply-templates select="*"/>
</xsl:copy>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="operation">create</xsl:attribute>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()"><!--identity for all other nodes-->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
t:\ftemp>rem Done!

XSL Transformation only for certain element based on attributes

I have a XML file for which transformation rules should be applied for certain elements only based on its attribute value and the rest should be retained as it is.
<bigdata>
<data>
<Object class="QWE" Name="Country-1/State-1/QWE-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="RTY" Name="Country-1/State-1/RTY-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="UIO" Name="Country-1/State-1/UIO-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="PAS" Name="Country-1/State-1/PAS-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
</data>
The above xml should be converted to below xml snippet where only xml element Object for which class equals QWE should be changed to POST.
ie only first element in must be changed based on its attribute.
Any advices on this would be gratefull
<bigdata>
<data>
<Object class="POST" Name="Country-1/State-1/POST-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="RTY" Name="Country-1/State-1/RTY-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="UIO" Name="Country-1/State-1/UIO-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="PAS" Name="Country-1/State-1/PAS-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
</data>
Try this:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Object/#class[. = 'QWE' ]">
<xsl:attribute name="class">
<xsl:value-of select="'POST'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<bigdata>
<data>
<Object class="POST" Name="Country-1/State-1/QWE-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="RTY" Name="Country-1/State-1/RTY-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="UIO" Name="Country-1/State-1/UIO-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
<Object class="PAS" Name="Country-1/State-1/PAS-1">
<p name="Map">20</p>
<p name="Required">0</p>
<p name="Combined">68</p>
<p name="State">0</p>
</Object>
</data>
</bigdata>
Update if the class value should be start with a string (QWE) and only this part should be replaced. Try this:
<xsl:template match="Object/#class[starts-with(., 'QWE') ]">
<xsl:attribute name="class">
<xsl:value-of select="'POST'"/>
<xsl:value-of select="substring-after(.,'QWE')"/>
</xsl:attribute>
</xsl:template>
Use following XSLT to transform above xml:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#class[parent::Object]">
<xsl:choose>
<xsl:when test=".='QWE'">
<xsl:attribute name="class">
<xsl:value-of select="'POST'"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
refer this for more info:
XSLT: How to change an attribute value during <xsl:copy>?

group width on xslt elements (xslt 1.0)

ХМL:
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
It is necessary to bring the country's selectors sorted by name and get rid of duplicates:
<select>
<option value="http://germany.gr">Germany</option>
<option value="france">France</option>
</select>
That is, if the country meets a few times, in value = specify the id. If once, then specify the link
Description grouped as follows: if a country meets several times
Code:
<div id="france">
<p> Lion</p>
<p>Paris</p>
</div>
If the country meets once - did not write
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kObjByCountry" match="Object" use="Property[#Name='COUNTRY']"/>
<xsl:template match="/">
<xsl:variable name="vCountries" select=
"/*/*/*/data/Object
[generate-id()
= generate-id(key('kObjByCountry', Property[#Name='COUNTRY'])[1])
]"/>
<select>
<xsl:apply-templates select="$vCountries">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</select>
<xsl:apply-templates select="$vCountries" mode="desc">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Object">
<xsl:variable name="v2nd"
select="key('kObjByCountry', Property[#Name='COUNTRY'])[2]"/>
<option value="{(#Id[$v2nd]|Property[#Name='WWW'])[1]}">
<xsl:value-of select="Property[#Name='COUNTRY']"/>
</option>
</xsl:template>
<xsl:template mode="desc"
match="Object[key('kObjByCountry', Property[#Name='COUNTRY'])[2]]">
<div id="sity_{#Id}">
<xsl:apply-templates select=
"key('kObjByCountry', Property[#Name='COUNTRY'])
/Property[#Name='NAME']">
<xsl:sort/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="Property[#Name='NAME']">
<p><xsl:value-of select="."/></p>
</xsl:template>
<xsl:template mode="desc"
match="Object[not(key('kObjByCountry', Property[#Name='COUNTRY'])[2])]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
produces the wanted, correct result -- in which the countries/cities are sorted:
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option>
</select>
<div id="sity_id1">
<p>Lion</p>
<p>Paris</p>
</div>
Explanation:
Proper use of the Muenchian grouping method and AVT s.
Proper use of xsl:sort.
With XSLT 1.0 you can use Muechian grouping to perform grouping respectively to identify and eliminate duplicates; the stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="#Id"/>
<xsl:template match="data">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1]) and key('k1', #Id)[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', #Id)[2]]">
<option value="{#Id}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', #Id)[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="city_{#Id}">
<xsl:apply-templates select="key('k1', #Id)/Property[#Name = 'NAME']" mode="desc"/>
</div>
</xsl:template>
<xsl:template match="data/Object/Property" mode="desc">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
transforms the input
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
into
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option></select><div id="city_id1">
<p>Paris</p>
<p>Lion</p>
</div>
[edit]
Here is an adapted stylesheet that uses a different key to try to implement the changed requirements:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="Property[#Name = 'COUNTRY']"/>
<xsl:template match="data[Object]">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1]) and key('k1', Property[#Name = 'COUNTRY'])[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', Property[#Name = 'COUNTRY'])[2]]">
<option value="{Property[#Name = 'COUNTRY']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', Property[#Name = 'COUNTRY'])[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="{Property[#Name = 'COUNTRY']}">
<xsl:apply-templates select="key('k1', Property[#Name = 'COUNTRY'])" mode="link">
<xsl:sort select="#Name"/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="data/Object" mode="link">
<p>
<a href="/index.php?id={#Id}">
<xsl:value-of select="#Name"/>
</a>
</p>
</xsl:template>
</xsl:stylesheet>
When I apply that stylesheet to the input
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
the result is
<select>
<option value="France">France</option>
<option value="http://germany.gr">Germany</option></select><div id="France">
<p>Lion</p>
<p>Paris</p>
</div>
so the option elements are grouped as required (although I couldn't figure out what determines the sort order) and the p elements contain links with the #Id value incorporated.