How to remove elements/attributes from sequence be

2019-08-21 13:01发布

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>

标签: xml xslt
1条回答
▲ chillily
2楼-- · 2019-08-21 13:30

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:

<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="*"/>

    <xsl:template match="/">
      <!-- first pass with default-mode templates -->
      <xsl:variable name="pass1">
        <xsl:apply-templates />
      </xsl:variable>
      <!-- second pass with dedup templates -->
      <xsl:apply-templates select="$pass1" mode="dedup" />
    </xsl:template>

    <!-- pre-processing templates -->

    <!-- 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())]"/>


    <!-- de-duplication templates -->

    <!-- Ignore element nodes which are deep-equal to a preceding sibling element -->
    <xsl:template mode="dedup" match="*[some $ps in preceding-sibling::* satisfies deep-equal(., $ps)]"/>

    <xsl:template mode="dedup" match="@*|node()">
      <xsl:copy><xsl:apply-templates mode="dedup" select="@*|node()"/></xsl:copy>
    </xsl:template>

</xsl:stylesheet>
查看更多
登录 后发表回答