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></OLDSIN开发者_如何学运维O>
</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 ofITEM
elements with the sameITEMCODE
-- 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 ofITEM
elements with the sameITEMCODE
and for whoseITEMCODE
there are items withACTION
both "ADD" and "DELETE". It constructs the necessaryOLDSINO
element by applying templates in mode "List" to allITEM
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 sameITEMCODE
.
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>
精彩评论