根据您对 Rubens Farias 的回答的评论(实际上,您应该编辑您的问题以包括在内),您似乎想要一种通用方法将任何一组相邻BulletText
元素转换为列表。这给我们带来了两个问题:我们如何找到这样的组,找到它们后,我们如何将它们转换为列表?
要找到一个组,我们需要查找BulletText
其前一个兄弟元素不是元素的所有BulletText
元素。每一个都开始一个组,这些是我们将要转换为列表的元素。所以我们要做的第一件事是创建一个 XPath 表达式来找到它们:
BulletText[not(preceding-sibling::*[1][name()='BulletText'])]
如果您查看该 XPath 表达式中的谓词,它正在做我所说的我们需要做的事情:BulletText
如果它的第一个前面的兄弟 ( preceding-sibling::*[1]
) 的名称不是BulletText
. 请注意,如果元素没有前面的兄弟元素,则此表达式仍将匹配它。
所以现在我们可以创建一个模板来匹配这些起始组元素。我们在这个模板中放了什么?我们要将这些元素转换为LIST
元素,所以模板开始看起来像:
<LIST>
...
</LIST>
很容易。但是我们如何找到要填充该列表的元素呢?我们必须处理两种情况。
第一个很简单:如果以下所有兄弟姐妹都是BulletText
元素,我们希望使用此元素及其所有以下兄弟姐妹填充列表。
第二个更难。如果后面的兄弟不是BulletText
元素,我们希望我们的列表是当前元素父元素的所有子元素,从当前元素开始,在停止元素之前结束。这是一个案例,我们需要使用count()
函数来计算开始和结束索引,以及position()
找到每个元素的位置的函数。
完成的模板如下所示:
<xsl:template match="BulletText[not(preceding-sibling::*[1][name()='BulletText'])]">
<!-- find the element that we want to stop at -->
<xsl:variable name="stop" select="./following-sibling::*[name() != 'BulletText'][1]"/>
<LIST>
<xsl:choose>
<!-- first, the simple case: there's no element we have to stop at -->
<xsl:when test="not($stop)">
<xsl:apply-templates select="." mode="item"/>
<xsl:apply-templates select="./following-sibling::BulletText" mode="item"/>
</xsl:when>
<!-- transform all elements between the start and stop index into items -->
<xsl:otherwise>
<xsl:variable name="start_index" select="count(preceding-sibling::*) + 1"/>
<xsl:variable name="stop_index" select="count($stop/preceding-sibling::*)"/>
<xsl:apply-templates select="../*[position() >= $start_index
and position() <= $stop_index]"
mode="item"/>
</xsl:otherwise>
</xsl:choose>
</LIST>
</xsl:template>
您需要另外两个模板。将BulletText
元素转换为项目 - 我们mode
在这里使用,以便我们可以将其应用于BulletText
元素而无需调用我们当前使用的模板:
<xsl:template match="BulletText" mode="item">
<ITEM>
<xsl:value-of select="."/>
</ITEM>
</xsl:template>
然后你还需要一个模板来防止我们的第一个模板不BulletText
匹配的元素生成任何输出(因为如果我们使用恒等变换,如果我们不使用它们只会被复制):
<xsl:template match='BulletText'/>
由于 XSLT 模板优先规则的魔力BulletText
,两个模板匹配的任何元素都将被第一个转换,而这个将捕获其余的。
只需将这三个模板添加到身份转换即可。