It seemed like an easy task but I am totally stuck now. I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<ITEM_CODE>ITEM_CODE</ITEM_CODE>
<ITEM_NAME>ITEM_NAME</ITEM_NAME>
<ITEM_ALTERNATE_NAME>ITEM_ALTERNATE_NAME</ITEM_ALTERNATE_NAME>
<ITEM_CATEGORY_CODE>ITEM_CATEGORY_CODE</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>15031</ITEM_CODE>
<ITEM_NAME>Outer Carton</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150529</ITEM_CODE>
<ITEM_NAME>Outer Carton</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150999</ITEM_CODE>
<ITEM_NAME>Outer Carton</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150988</ITEM_CODE>
<ITEM_NAME>test</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
</Items>
If <ITEM_NAME>
elements have duplicate contents those should be renamed with a suffix, e.g. a counter value. I came up with this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" method="xml" indent="yes"/>
<xsl:key name="keyItemName" match="Item" use="concat(ITEM_CODE , '|', ITEM_NAME)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Items">
<Items>
<xsl:apply-templates select="@*|node()"/>
</Items>
</xsl:template>
<xsl:template match="ITEM_NAME">
<xsl:for-each select="parent::Item[generate-id()=generate-id(key('keyItemName',concat(ITEM_CODE , '|', ITEM_NAME))[1])]">
<xsl:variable name="number">
<xsl:number/>
</xsl:variable>
<ITEM_NAME>
<xsl:value-of select="concat(ITEM_NAME,'-',$number)"/>
</ITEM_NAME>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It gives me this output:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<ITEM_CODE>ITEM_CODE</ITEM_CODE>
<ITEM_NAME>ITEM_NAME-1</ITEM_NAME>
<ITEM_ALTERNATE_NAME>ITEM_ALTERNATE_NAME</ITEM_ALTERNATE_NAME>
<ITEM_CATEGORY_CODE>ITEM_CATEGORY_CODE</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>15031</ITEM_CODE>
<ITEM_NAME>Outer Carton-2</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150529</ITEM_CODE>
<ITEM_NAME>Outer Carton-3</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150999</ITEM_CODE>
<ITEM_NAME>Outer Carton-4</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150988</ITEM_CODE>
<ITEM_NAME>test-5</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
</Items>
But I expect this output:
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item>
<ITEM_CODE>ITEM_CODE</ITEM_CODE>
<ITEM_NAME>ITEM_NAME</ITEM_NAME>
<ITEM_ALTERNATE_NAME>ITEM_ALTERNATE_NAME</ITEM_ALTERNATE_NAME>
<ITEM_CATEGORY_CODE>ITEM_CATEGORY_CODE</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>15031</ITEM_CODE>
<ITEM_NAME>Outer Carton-2</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150529</ITEM_CODE>
<ITEM_NAME>Outer Carton-3</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150999</ITEM_CODE>
<ITEM_NAME>Outer Carton-4</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
<Item>
<ITEM_CODE>150988</ITEM_CODE>
<ITEM_NAME>test</ITEM_NAME>
<ITEM_ALTERNATE_NAME/>
<ITEM_CATEGORY_CODE>52401</ITEM_CATEGORY_CODE>
</Item>
</Items>
In the last <Item>
the ITEM_NAME
should not be renamed because it is not called "Outer Carton". Also in the first <Item>
element no renaming should be happening.
I am close to the solution but I just can't get around to it. Can someone please help - thanks a lot!
Best regards, Peter
Using
preceding::
orpreceding-sibling::
to count prior instances is not very efficient computationally, but I don't see a way around it here. The approach below does have the benefit that it only counts preceding instances when after checking (with a key, which is very quick) that there are other items with the same name:When run on your sample input, this produces:
You current key seems to join ITEM_NAME and ITEM_CODE, but it looks like you only want ITEM_NAME here
It also looks like you want the numbering for the suffix to be based on the position of the parent item element. One way to achieve this is to have a template to match the item element, and then pass the number as a parameter to the subsequent mathching templates
Then, you need a template to match ITEM_NAME elements for which duplicate occurs. This can be done simply by checking there is at least a second element defined in the group for the key:
Then, you can just output the element with the suffix.
Here is the full XSLT
When applied to your XML, the following is output