I want the output xml to have grouped for the element \'c\', according to the attribute \'f\'. Here is my input xml and the xslt. I want the group to occur only once and the ot
A simple solution would be to just add an empty template for all following c
nodes:
<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() > 1])]" />
You can match <a>
elements and check whether there are any preceding siblings with the same f
attribute in their <c>
sub-elements. If there are, you've found a duplicate of a given f
value (an occurrence of a given f
value that is not the first occurrence of that value) and you can just override the identity template to skip the element:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/>
</xsl:stylesheet>
An advantage of this solution is that it doesn't require any knowledge about key or ID generation; it just works with basic XPath axis features. However, it might get slightly more complicated when the elements to compare are not all on the same nesting depth/in the same relative element hierarchy.
P.S.: I removed the <xsl:strip-space elements="*"/>
element because I couldn't test it (my Xml processor claimed I can only use it if I pass a readable stream rather than a file), but feel free to re-insert it if it works for you.
This transformation:
<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="kAByC-F" match="a" use="*/c/@f"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"a[*/c
and
not(generate-id()
=
generate-id(key('kAByC-F', */c/@f)[1])
)
]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse></somethingelse>
</b>
</a>
</M>
produces the wanted, correct result:
<M>
<a>
<b>
<c f="123">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<c f="567">
<d>Al</d>
<e NO="678">
<f>Y</f>
<g>
<h>FTO</h>
</g>
</e>
</c>
</b>
</a>
<a>
<b>
<somethingelse/>
</b>
</a>
</M>
Explanation:
Proper use of the Muenchian grouping method.
One idea might be to save all values of c in a variable in a format that allows you to differentiate them from one-another, and then every time you encounter c, you check to see if that value is contained within the variable. If it is, skip to the next node. If it isn't, continue processing the current node.
Tell me if you need more specific information
EDIT: As an alternative, and probably an easier method (I've been using NAnt recently, so I might be giving you a NAnt strategy) is to sort all the nodes by their s values. Then just have a variable that stores the current value of c and compare until the value you're looking at isn't equal to the stored value. Then reassign the value and do it again!