This question is an extension of the question here. The answer @Martin Honnen provided here is does almost exactly what I want, though when deep-equal
is called I incorrectly expected the elements/attributes that I wanted to be removed by the previous template to have already been removed (and thus non-existent in the sequences being passed into deep-equal
).
How can I remove elements/attributes from a sequence being passed into deep-equal
, or otherwise tell deep-equal
to ignore certain elements/attributes?
XSL:
<!--
When a file is transformed using this stylesheet the output will be
formatted as follows:
1.) Elements named "info" will be removed
2.) Attributes named "file_line_nr" or "file_name" will be removed
3.) Comments will be removed
4.) Processing instructions will be removed
5.) XML declaration will be removed
6.) Extra whitespace will be removed
7.) Empty attributes will be removed
8.) Elements which have no attributes, child elements, or text will be removed
9.) Duplicate sibling elements will be removed
10.) All elements will be sorted by name recursively
11.) All attributes will be sorted by name
-->
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Set output options -->
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<!-- Match any attribute -->
<xsl:template match="@*">
<xsl:copy/>
</xsl:template>
<!-- Match any element -->
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
<xsl:for-each-group select="node() except (processing-instruction(), comment())" group-adjacent="boolean(self::*)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<!-- Elements/attributes to ignore -->
<xsl:template match="@*[normalize-space()='']|info|@file_line_nr|@file_name|*[not(@*|node())]"/>
<!-- Ignore element nodes which are deep-equal to a preceding sibling element -->
<xsl:template match="*[some $ps in preceding-sibling::* satisfies deep-equal(., $ps)]"/>
</xsl:stylesheet>
XML Input:
<root>
<!-- foo #1 -->
<foo a="a" file_line_nr="1"/>
<!-- bar #1 -->
<bar>
some text
<info a="a"/>
</bar>
<!-- foo #2 -->
<foo a="a" file_line_nr="2"/><!-- This should be removed because it is identical to the foo #1 except for the "file_line_nr" attribute which should be removed/ignored -->
<!-- baz #1 -->
<baz file_name="some_file.h">
<bam a="a"/>
</baz>
<!-- bar #2 -->
<bar><!-- This should be removed because it is identical to the bar #1 except for the "info" child element which should be removed/ignored -->
some text
<info b="b"/>
</bar>
<!-- baz #2 -->
<baz file_name="some_other_file.h"><!-- This should be removed because it is identical to the baz #1 except for the "file_name" attribute which should be removed/ignored -->
<bam a="a"/>
</baz>
</root>
Desired Output:
<root>
<foo a="a"/>
<bar>
some text
</bar>
<baz>
<bam a="a"/>
</baz>
</root>
Your templates all match against nodes in the original XML tree, not nodes you have output. You will have to split the processing into two phases, first do all the stripping of unwanted elements, attributes, etc. into a variable, then do the deep-equal filter on that variable. Something like this: