copy a descendant node and place it after the current node - xslt

In the below xml. I want to do following
match wrapper/item whose child or descendant is <olr>.
If found, copy <olr> and all its following-siblings, and paste it
after the current node i.e. end of wrapper/item. (other word,<olr> and its following-siblings will always be direct child of <wrapper>)
rest should be copied as it is.
Input XML:
<root>
<wrapper>
<item>
<item>1.1</item>
<item>
<olr>outlier1</olr>
</item>
</item>
<item>
<item>2.1</item>
<item>
<item>
<item>
<item>preceedingsibling1</item>
<item>preceedingsibling2</item>
<olr>outlier2</olr>
<item>followingsibling1</item>
<item>followingsibling2</item>
</item>
</item>
</item>
</item>
<item>
<item>3.1</item>
<item>
<item>
<item>3.3.1</item>
</item>
</item>
</item>
</wrapper>
<root>
<wrapper>
<item>
<item>1.1</item>
<item>
</item>
</item>
<olr>outlier1</olr>
<item>
<item>2.1</item>
<item>
<item>
<item>
<item>preceedingsibling1</item>
<item>preceedingsibling2</item>
</item>
</item>
</item>
</item>
<olr>outlier2</olr>
<item>followingsibling1</item>
<item>followingsibling2</item>
<item>
<item>3.1</item>
<item>
<item>
<item>3.3.1</item>
</item>
</item>
</item>
</wrapper>
I am trying something:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[descendant::olr]">
<xsl:apply-templates select="node() except descendant::olr"/>
<!-- ? not sure what to do here -->
</xsl:template>

The below XSLT-2.0 solution would do this:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<!-- identity transform template with mode='olr' -->
<xsl:template match="#* | node()" mode="olr">
<xsl:copy>
<xsl:apply-templates select="#*, node()[1]" mode="olr"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]" mode="olr"/>
</xsl:template>
<!-- identity transform template -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<!-- copy the olr and it's following-elements in the current element -->
<xsl:template match="wrapper/item[descendant::olr]">
<xsl:copy>
<xsl:apply-templates select="#*, node()[1]" />
</xsl:copy>
<xsl:apply-templates select="descendant::olr[not(preceding-sibling::olr)]" mode="olr"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<!-- "do nothing for olr" template -->
<xsl:template match="olr[ancestor::item/parent::wrapper]"/>
</xsl:stylesheet>
There are two identity transform templates(1st and 2nd) to copy the elements as-is, recursively.
The third template matches the wrapper/item with descendant::olr and specially processes olr(and its following-siblings) by copying it as its own following-sibling.
The fourth template is to do nothing for olr in the normal process.

One way to achieve this is with
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="wrapper/item[descendant::olr]">
<xsl:next-match/>
<xsl:copy-of select="descendant::olr/(., following-sibling::node())"/>
</xsl:template>
<xsl:template match="wrapper/item//olr | wrapper/item//node()[preceding-sibling::olr]"/>
</xsl:transform>
http://xsltransform.net/bFWR5Fg. I am not sure what happens if you have several olr elements.

Related

Find unique child nodes of an element

Need to write an xslt file. Below is the input file:
<assets>
<item>
<child1>some text</child1>
<child2>some text</child2>
<child3>some text</child3>
<child4>some text</child4>
</item>
<item>
<child1>some text</child1>
<child2>some text</child2>
<childx>some text</childx>
</item>
<item>
<child1>some text</child1>
<childx>some text</childx>
<childy>some text</childy>
<childz>some text</childz>
</item>
</assets>
I need to find out all the unique child names of assets/item. The number of children and child name is dynamic under the element (item)
The Output should be as below:
<item>
<columns>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</columns>
</item>
You can use Muenchian grouping on the element names - something like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="elements" match="*" use="local-name()" />
<xsl:template match="/">
<item>
<xsl:apply-templates select="assets/item" />
</item>
</xsl:template>
<xsl:template match="item">
<xsl:for-each select="*[count(.|key('elements', local-name())[1]) = 1]">
<columnname>
<xsl:value-of select="name()"/>
</columnname>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Will get you this:
<item>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</item>
you can use this
<xsl:key name="child" match="item/*" use="name()"/>
<xsl:template match="/">
<item>
<columns>
<xsl:for-each select="//item/*[count(.|key('child', name())[1]) = 1]">
<columnname><xsl:value-of select="name()"/></columnname>
</xsl:for-each>
</columns>
</item>
</xsl:template>
output
<item>
<columns>
<columnname>child1</columnname>
<columnname>child2</columnname>
<columnname>child3</columnname>
<columnname>child4</columnname>
<columnname>childx</columnname>
<columnname>childy</columnname>
<columnname>childz</columnname>
</columns>
</item>

Remove parent node if child node is empty

I am trying to transform a given XML using xslt. The caveat is that I would have to delete a parent node if a given child node is not present. I did do some template matching, but I am stuck. Any help would be appreciated.
The input xml :
<?xml version="1.0" encoding="UTF-8"?>
<main>
<item>
<value>
<item>
<value>ABC</value>
<key>test1</key>
</item>
<item>
<value>XYZ</value>
<key>test2</key>
</item>
<item>
<value></value>
<key>test3</key>
</item>
</value>
</item>
<item>
<value />
<key>test4</key>
</item>
<item>
<value>PQR</value>
<key>test5</key>
</item>
</main>
Expected Output:
<?xml version="1.0" encoding="UTF-8"?>
<main>
<item>
<value>
<item>
<value>ABC</value>
<key>test1</key>
</item>
<item>
<value>XYZ</value>
<key>test2</key>
</item>
</value>
</item>
<item>
<value>PQR</value>
<key>test5</key>
</item>
</main>
The issue is if I use template matching e.g.
<xsl:template match="item[not(value)]"/> as mentioned in deleting the parent node if child node is not present in xml using xslt, then it completely removes everything as main/item/value is also empty.
What I need is remove if element is empty but only do if element has no child element.
You should first start with the XSLT identity template
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Then, all you need is a template that matches the item element where all descendent leaf value elements are empty.
<xsl:template match="item[not(descendant::value[not(*)][normalize-space()])]" />
So, the template matches it, but doesn't output it.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="item[not(descendant::value[not(*)][normalize-space()])]" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think you want to remove the element of it has no children at all (whether those children be elements or text nodes). Try inserting this template:
<xsl:template match="item">
<xsl:if test="exists(value/node())">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:if>
</xsl:template>
If I read this correctly, you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[not(value[node()])]"/>
</xsl:stylesheet>
This will remove any item that does not have a value child with some content.

how to modify one xml input using variable from second xml input in XSLT?

My question is how to change values in <color> in sample1.xml based on the same <id> in sample2.xml
sample1.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>red</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>green></color>
</item>
</root>
sample 2.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>3</id>
<color>white</color>
</item>
</root>
expected output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>white></color>
</item>
</root>
I only know how to copy entire sample1.xml to output, but I don't know how to remember ids from sample2.xml, and by that values make changes to sample1.
Don't know if is possible, but probably I must use variables on some unknown way.
Here is my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml" />
<xsl:param name="sample1"/>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2"/>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/> <!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I really don't have idea what is the right way to do that, because I am new to XSLT 2.0. Any help will be much appreciated.
I was beaten to this by Martin Honnen, but here's my solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml"/>
<xsl:param name="sample1"/>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2"/>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/>
<!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/item/color">
<xsl:param name="id" select="parent::item/id/text()"/>
<xsl:copy>
<xsl:choose>
<xsl:when test="$sample2_xml/root/item[id=$id]/color">
<xsl:value-of select="$sample2_xml/root/item[id=$id]/color/text()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
Define a key <xsl:key name="id" match="item" use="id"/> and then add a template
<xsl:template match="item[key('id', id, $sample2_xml)]/color">
<xsl:copy-of select="key('id', ../id, $sample2_xml)/color"/>
</xsl:template>
So the complete sample is
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs saxon"
xmlns:saxon="http://saxon.sf.net/"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml" />
<xsl:param name="sample1" as="xs:string"><![CDATA[<root>
<item>
<id>1</id>
<color>red</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>green></color>
</item>
</root>]]></xsl:param>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2" as="xs:string"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>3</id>
<color>white</color>
</item>
</root>]]></xsl:param>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:key name="id" match="item" use="id"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/> <!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[key('id', id, $sample2_xml)]/color">
<xsl:copy-of select="key('id', ../id, $sample2_xml)/color"/>
</xsl:template>
</xsl:stylesheet>

how to check and replace the null date using xslt

I want to check if my expiry date is null than i want to take the value from CreationDate.
My XML is like
<CreationDate>2017-03-18</CreationDate> <ExpiresDate>20170318</ExpiresDate>
and in my xsl
<xsl:element name="NewDeliveryDueDate">
<xsl:call-template name="FormatDate">
<xsl:with-param name="DateTime" select="Product/ExpiresDate"/>
</xsl:call-template>
</xsl:element>
&comma;
Please suggest.
Your question is incomplete. Perhaps this could work for you:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ExpiresDate[not(string())]">
<xsl:copy>
<xsl:value-of select="translate(../CreationDate, '-', '')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this is applied to the following example input:
<root>
<item>
<CreationDate>2017-03-18</CreationDate>
<ExpiresDate>20170318</ExpiresDate>
</item>
<item>
<CreationDate>2017-03-09</CreationDate>
<ExpiresDate/>
</item>
</root>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<CreationDate>2017-03-18</CreationDate>
<ExpiresDate>20170318</ExpiresDate>
</item>
<item>
<CreationDate>2017-03-09</CreationDate>
<ExpiresDate>20170309</ExpiresDate>
</item>
</root>

Merging duplicate nodes in an to form a new XML using XSLT

i have an XML as below. Where differenct items come for delete or add. each item will have serial number. what i trying to do is i want to prepare a new xml from these input in such a way that, if an item with an item code has come for add and delete i want to merge them into a single item node. The new item formed will have action code as update and sino will be add item sino and oldsino will be delete item sino.
INPUT
`
<ITEM>
<SINO>1</SINO>
<ITEMCODE>101</ITEMNAME>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>2</SINO>
<ITEMCODE>101</ITEMNAME>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>3</SINO>
<ITEMCODE>102</ITEMNAME>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>4</SINO>
<ITEMCODE>103</ITEMNAME>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>5</SINO>
<ITEMCODE>103</ITEMNAME>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>6</SINO>
<ITEMCODE>104</ITEMNAME>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
`
OUTPUT
`
<ITEM>
<SINO>1</SINO>
<ITEMCODE>101</ITEMNAME>
<ACTION>UPDATE</ACTION>
<OLDSINO>2</OLDSINO>
</ITEM>
<ITEM>
<SINO>3</SINO>
<ITEMCODE>102</ITEMNAME>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>4</SINO>
<ITEMCODE>103</ITEMNAME>
<ACTION>DELETE</ACTION>
<OLDSINO>5</OLDSINO>
</ITEM>
<ITEM>
<SINO>6</SINO>
<ITEMCODE>104</ITEMNAME>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
`
Any ideas how to acheive this using XSLT.
if an item with an item code has come for add and delete i want to merge them into a single item node. The new item formed will have action code as update and sino will be add item sino and oldsino will be delete item sino
This solution mainly plays with template match patterns on siblings, and exploits the identity rule.
XSLT 1.0
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM[
ITEMCODE[../ACTION='ADD']
=
../ITEM/ITEMCODE[../ACTION='DELETE']]">
<xsl:copy>
<SINO><xsl:value-of select="../ITEM[ITEMCODE
=current()/ITEMCODE and ACTION='ADD']/
SINO"/>
</SINO>
<xsl:copy-of select="ITEMCODE"/>
<ACTION>UPDATE</ACTION>
<OLDSINO><xsl:value-of select="../ITEM[ITEMCODE
=current()/ITEMCODE and ACTION='DELETE']/
SINO"/>
</OLDSINO>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM[
ITEMCODE[../ACTION='DELETE']
=
../ITEM/ITEMCODE[../ACTION='ADD']]"/>
</xsl:stylesheet>
The solution produces:
<?xml version="1.0" encoding="UTF-16"?>
<ITEMS>
<ITEM>
<SINO>1</SINO>
<ITEMCODE>101</ITEMCODE>
<ACTION>UPDATE</ACTION>
<OLDSINO>2</OLDSINO>
</ITEM>
<ITEM>
<SINO>3</SINO>
<ITEMCODE>102</ITEMCODE>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>4</SINO>
<ITEMCODE>103</ITEMCODE>
<ACTION>UPDATE</ACTION>
<OLDSINO>5</OLDSINO>
</ITEM>
<ITEM>
<SINO>6</SINO>
<ITEMCODE>104</ITEMCODE>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
</ITEMS>
Here is a short and simple solutions that overrides the identity rule/template:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kItemByCode" match="ITEM"
use="ITEMCODE"/>
<xsl:key name="kActionByCode" match="ACTION"
use="../ITEMCODE"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"ITEM[not(generate-id() =
generate-id(key('kItemByCode', ITEMCODE)[1])
)
]"/>
<xsl:template match=
"ITEM[generate-id() =
generate-id(key('kItemByCode', ITEMCODE)[1])
and
key('kActionByCode', ITEMCODE) = 'ADD'
and
key('kActionByCode', ITEMCODE) = 'DELETE'
]
/ACTION/text()
">
<xsl:text>UPDATE</xsl:text>
</xsl:template>
<xsl:template match=
"ITEM[generate-id() =
generate-id(key('kItemByCode', ITEMCODE)[1])
and
key('kActionByCode', ITEMCODE) = 'ADD'
and
key('kActionByCode', ITEMCODE) = 'DELETE'
]
/OLDSINO
">
<OLDSINO>
<xsl:apply-templates mode="List"
select="key('kItemByCode', ../ITEMCODE)[position()>1]" />
</OLDSINO>
</xsl:template>
<xsl:template match="ITEM" mode="List">
<xsl:if test="not(position()=1)">,</xsl:if>
<xsl:value-of select="SINO"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document (based on and correcting the provided severely malformed XML fragment):
<t>
<ITEM>
<SINO>1</SINO>
<ITEMCODE>101</ITEMCODE>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>2</SINO>
<ITEMCODE>101</ITEMCODE>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>3</SINO>
<ITEMCODE>102</ITEMCODE>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>4</SINO>
<ITEMCODE>103</ITEMCODE>
<ACTION>ADD</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>5</SINO>
<ITEMCODE>103</ITEMCODE>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
<ITEM>
<SINO>6</SINO>
<ITEMCODE>104</ITEMCODE>
<ACTION>DELETE</ACTION>
<OLDSINO></OLDSINO>
</ITEM>
</t>
the wanted, correct result is produced:
<t>
<ITEM>
<SINO>1</SINO>
<ITEMCODE>101</ITEMCODE>
<ACTION>UPDATE</ACTION>
<OLDSINO>2</OLDSINO>
</ITEM>
<ITEM>
<SINO>3</SINO>
<ITEMCODE>102</ITEMCODE>
<ACTION>ADD</ACTION>
<OLDSINO />
</ITEM>
<ITEM>
<SINO>4</SINO>
<ITEMCODE>103</ITEMCODE>
<ACTION>UPDATE</ACTION>
<OLDSINO>5</OLDSINO>
</ITEM>
<ITEM>
<SINO>6</SINO>
<ITEMCODE>104</ITEMCODE>
<ACTION>DELETE</ACTION>
<OLDSINO />
</ITEM>
</t>
Explanation:
The identity rule/template copies every node "as-is".
A template with empty body overrides the identity rule for every non-first ITEM of a group of ITEM elements with the same ITEMCODE -- any such node isn't copied to the output (aka deleted).
A third template overrides the identity rule for every ITEM element that is the first in a group of ITEM elements with the same ITEMCODE and for whose ITEMCODE there are items with ACTION both "ADD" and "DELETE". It constructs the necessary OLDSINO element by applying templates in mode "List" to all ITEM elements in the group, but the first one.
The Muenchian method for grouping and generally keys are used so that an efficient and compact implementation is achieved.
This solution works correctly with any arbitrary mixture of ITEM elements that may have the same ITEMCODE.
Correcting for the typo (opening ITEMCODE tag and closing ITEMNAME tag) and working with a root element of ITEMS a complete XSLT could be:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="deletes" match="ITEM[ACTION='DELETE']" use="ITEMCODE/text()"/>
<xsl:key name="adds" match="ITEM[ACTION='ADD']" use="ITEMCODE/text()"/>
<xsl:template match="ITEMS">
<ITEMS>
<xsl:apply-templates select="ITEM" />
</ITEMS>
</xsl:template>
<xsl:template match="ITEM[ACTION='ADD' and key('deletes',ITEMCODE/text())]">
<xsl:variable name="current" select="ITEMCODE/text()"/>
<xsl:copy>
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="name()='ACTION'">
<ACTION>UPDATE</ACTION>
</xsl:when>
<xsl:when test="name()='OLDSINO'">
<OLDSINO><xsl:value-of select="key('deletes',$current)/SINO/text()"/></OLDSINO>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM[ACTION='ADD' and not(key('deletes',ITEMCODE/text()))]">
<xsl:copy-of select="." />
</xsl:template>
<xsl:template match="ITEM[ACTION='DELETE' and not(key('adds',ITEMCODE/text()))]">
<xsl:copy-of select="." />
</xsl:template>
<xsl:template match="ITEM" />
</xsl:stylesheet>