从开头和结尾删除某些节点并删除其间的重复项

Delete certain nodes from the beginning and the end and remove duplicates in between

考虑这个 XML:

<a>
   <b>something</b>
   <b>something</b>
   <M>other</M>
   <b>something</b>
   <b>something</b>
   <N>else</N>
   <b>something</b>
   <b>something</b>
   <b>something</b>
</a>

现在我想删除开头或结尾的所有 "b",如果它们位于不同节点之间,我只想输出一个。所以这将是所需的输出:

<a>
   <M>other</M>
   <b>something</b>
   <N>else</N>
</a>

我不知道有多少 "b" 也不知道其他节点的名称是什么。开始很简单:当 "b" 跟随 "b":

时删除所有 "b"
<xsl:template match="b[preceding-sibling::*[1][self::b]]"/>

然后去掉第一个"b":

<xsl:template match="b[position= 1]"/>

最后一个如果只有一个"b":

<xsl:template match="b[position= last()]"/>

但我无法摆脱最后三个 "b" 中的第一个。测试应该说:如果只有 "b" 但没有其他内容,则删除 "b"。我发现的一些解决方案利用了分组,但由于节点的顺序很重要,因此在这里似乎无济于事。

我建议使用xsl:for-each-group如下:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output indent="yes"/>

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

    <xsl:template match="a">
        <xsl:copy>
            <xsl:for-each-group select="*" group-adjacent="boolean(self::b)">
                <xsl:choose>
                    <xsl:when test="not(current-grouping-key())">
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:when>
                    <xsl:when test="not(position() = (1, last()))">
                        <xsl:apply-templates select="."/>
                    </xsl:when>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

这改变了

<a>
    <b>1</b>
    <b>2</b>
    <M>foo</M>
    <b>3</b>
    <b>4</b>
    <N>bar</N>
    <b>5</b>
    <b>6</b>
    <b>7</b>
</a>

进入

<a>
   <M>foo</M>
   <b>3</b>
   <N>bar</N>
</a>