Here is the solution using the keys . In short (28% fewer lines of code and does not require horizontal scrolling). More reliable (see the end of this answer for details)
This is more general because it will work even when there are other elements between the elements that we want to group that need to be ignored (that is, where preceding-sibling::*[1] may be the element we want exclude from grouping - in the current problem - not the RECORD element):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="kStartGroup" match="/*/*" use= "generate-id(preceding-sibling::* [not(concat(E1, '|', E2, '|', E4, '|', E5) = concat(current()/E1, '|', current()/E2, '|', current()/E4, '|', current()/E5) ) ][1])"/> <xsl:template match="*[not(concat(E1, '|', E2, '|', E4, '|', E5) = concat(preceding-sibling::*[1]/E1, '|', preceding-sibling::*[1]/E2, '|', preceding-sibling::*[1]/E4, '|', preceding-sibling::*[1]/E5) )]"> <xsl:copy> <xsl:copy-of select="E1 | E2 | E4 | E5"/> <F><xsl:copy-of select= "key('kStartGroup', generate-id(preceding-sibling::*[1]))/F1"/></F> </xsl:copy> </xsl:template> <xsl:template match="/*"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:template> <xsl:template match="text()"/> </xsl:stylesheet>
Reliability / Scalability
Since this transformation does not contain recursion (nested calls <xsl:apply-templates ), it is reliable and scalable when applied to large XML files.
On the other hand, when re-converting in a sufficiently large XML document, in another answer โrecursion guysโ , a crash occurs due to . In my case, this failure was observed with the original XML document of about 13,000 (13 thousand lines) - this may vary depending on the available RAM, XSLT processor, etc.
The current conversion is successfully performed even on extremely large XML documents, for example, with 1,200,000 (one million and 200 thousand lines).