开发者

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:

  1. The identity rule/template copies every node "as-is".

  2. 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).

  3. 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.

  4. The Muenchian method for grouping and generally keys are used so that an efficient and compact implementation is achieved.

  5. 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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜