Reputation: 867
I have some XML data similar to the following, stored inside a variable in xslt 1.
<?xml version="1.0" encoding="utf-8"?>
<ItemCollection>
<Item>
<Number>1</Number>
<Data>string 1</Data>
</Item>
<Item>
<Number>1</Number>
<Data>string 2</Data>
</Item>
<Item>
<Number>2</Number>
<Data>string 3</Data>
</Item>
<Item>
<Number>2</Number>
<Data>string 3</Data>
</Item>
<Item>
<Number>3</Number>
<Data>string 4</Data>
</Item>
</ItemCollection>
This variable is called $collection
.
I want to transform the data so it will have a structure as shown below:
<?xml version="1.0" encoding="utf-8"?>
<ItemCollection>
<Item>
<Number>1</Number>
<Data>string 1</Data>
<Data>string 2</Data>
</Item>
<Item>
<Number>2</Number>
<Data>string 3</Data>
</Item>
<Item>
<Number>3</Number>
<Data>string 4</Data>
</Item>
</ItemCollection>
That is: Data
's who have the same sibling Number
(in value) should be grouped together. Duplicates of Item
's should be removed entirely, if the Number
and the Data
both are similar. In the original XML, each Item
has exactly one Number
and one Data
- I want to change this so it will still only have one Number
, but several Data
.
The XML data is, as said, inside a variable, which lies above. I've tried different things such as using two xsl:for-each
to iterate the variable twice (using exsl:node-set($collection)//Item
), but this only yielded a sorted order of the data, which is not of much use.
This is the XSLT I currently have (only a snippet, can post more if necessary)
<xsl:variable name="collection" select="'XMl DATA'"/>
<xsl:variable name="final">
<xsl:for-each select="exsl:node-set($collection)//Item">
<xsl:variable name="outer" select="."/>
<xsl:for-each select="exsl:node-set($collection)//Item">
<xsl:variable name="inner" select="."/>
<xsl:if test="$inner = $outer">
<Item>
<xsl:value-of select="$outer/Number"/>
<xsl:copy-of select="$inner/Data"/>
</Item>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="$final" />
Edit:
Thanks alot, for the answer. I realized that the result contains duplicates like so:
<Item>
<Number>2039205</Number>
<Data>String 1</Data>
<Data>String 1</Data>
<Data>String 1</Data>
</tns:Item>
Is there an easy way o remove these as well? I tried declaring another xsl:key
<xsl:key name="item-by-data" match="Item" use="Data"/>
and then do:
<xsl:template match="Item">
<xsl:copy>
<xsl:copy-of select="Number| key('item-by-data', Number)/Data[generate-id() = generate-id(key('item-by-data', Data)[1])]"/>
</xsl:copy>
</xsl:template>
But that removed all of the items, and not just the duplicates.
Upvotes: 0
Views: 70
Reputation: 167506
Use Muenchian grouping on the node-set you get with e.g. <xsl:param name="col-ns" select="exsl:node-set($collection)"/>
, that is, define a key
<xsl:key name="item-by-number" match="Item" use="Number"/>
then have
<xsl:template match="ItemCollection">
<xsl:copy>
<xsl:apply-templates select="Item[generate-id() = generate-id(key('item-by-number', Number)[1])]"/>
</xsl:copy>
</xsl:template>
and
<xsl:template match="Item">
<xsl:copy>
<xsl:copy-of select="Number | key('item-by-number', Number)/Data"/>
</xsl:copy>
</xsl:template>
then you just need to have <xsl:apply-templates select="$col-ns/node()"/>
in the place where you need to transform your content.
Complete sample is at http://xsltransform.net/gWmuiJc.
If you also want to eliminate duplicate Data
then you can use <xsl:key name="numbered-items-by-data" match="Item" use="concat(Number, '|', Data)"/>
and
<xsl:template match="Item">
<xsl:copy>
<xsl:copy-of select="Number | key('item-by-number', Number)[generate-id() = generate-id(key('numbered-items-by-data', concat(Number, '|', Data))[1])]/Data"/>
</xsl:copy>
</xsl:template>
a sample is at http://xsltransform.net/gWmuiJc/2.
Upvotes: 1