我知道之前已经回答过,但我很好奇如何在没有钥匙的情况下实现这一目标。
我有这个XML,我必须在{%tab%}和{%endtab%}之间获取节点。
<element>
<hello>{%tab%}</hello>
<hello>yes</hello>
<hello>{%endtab%}</hello>
<hello>no</hello>
<hello>no</hello>
<hello>{%tab%}</hello>
<hello>yes</hello>
<hello>{%endtab%}</hello>
</element>
这就是我得到的:
<xsl:template match="hello[preceding-sibling::hello[not(normalize-space(text())!='{%tab%}')]]
[following-sibling::hello[not(normalize-space(text())!='{%endtab%}')]]
[
(preceding-sibling::hello[not(normalize-space(text())!='{%tab%}')])[1]/(following-sibling::hello[not(normalize-space(text())!='{%endtab%}')])[1]
= (following-sibling::hello[not(normalize-space(text())!='{%endtab%}')])[1]
]">
<strong><xsl:value-of select="." /></strong>
</xsl:template>
这将选择{%endtab%}后面跟有{%tab%}个节点的所有节点。
hello[preceding-sibling::hello[not(normalize-space(text())!='{%tab%}')]]
[following-sibling::hello[not(normalize-space(text())!='{%endtab%}')]]
这个问题是“no”节点也被选中,因为它们也在这些节点之间。因此,我必须确保在某个节点(第一次出现)之后的{%endtab%}与前面的{%tab%}之后的{%endtab%}相同,我使用此XPath:
[
(preceding-sibling::hello[not(normalize-space(text())!='{%tab%}')])[1]
/(following-sibling::hello[not(normalize-space(text())!='{%endtab%}')])[1]
= (following-sibling::hello[not(normalize-space(text())!='{%endtab%}')])[1]
]
但是,这并没有按预期过滤“否”节点。
答案 0 :(得分:3)
忽略以下兄弟并计算前面的tab
和前面的endtab
。如果它们不相等,则您可以是endtab
或yes
。排除您endtab
的情况。
请允许我免除normalize-space()
以使示例更清晰。
<xsl:template match="hello[
(. != '{%endtab%}')
and
(
count(preceding-sibling::hello[. = '{%tab%}'])
!= count(preceding-sibling::hello[. = '{%endtab%}'])
)
]">
答案 1 :(得分:2)
此XPath表达式:
/*/*[not(. = '{%tab%}' or . = '{%endtab%}')
and preceding-sibling::*[. = '{%tab%}' or . = '{%endtab%}'][1] = '{%tab%}'
and following-sibling::*[. = '{%tab%}' or . = '{%endtab%}'][1] = '{%endtab%}'
]
选择任何元素作为top元素的子元素,并且位于具有字符串值{%tab%}
的兄弟元素和具有字符串值{%endtab%}
这是一个带有简单XSLT转换的运行证明:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"/*/*[not(. = '{%tab%}' or . = '{%endtab%}')
and preceding-sibling::*[. = '{%tab%}' or . = '{%endtab%}'][1] = '{%tab%}'
and following-sibling::*[. = '{%tab%}' or . = '{%endtab%}'][1] = '{%endtab%}'
]"/>
</xsl:template>
</xsl:stylesheet>
在提供的源XML文档上应用此转换时:
<element>
<hello>{%tab%}</hello>
<hello>yes</hello>
<hello>{%endtab%}</hello>
<hello>no</hello>
<hello>no</hello>
<hello>{%tab%}</hello>
<hello>yes</hello>
<hello>{%endtab%}</hello>
</element>
产生了想要的正确结果:
<hello>yes</hello>
<hello>yes</hello>
答案 2 :(得分:2)
一般感兴趣的两个注释:
我知道之前已经回答了这个问题,但我很好奇 没有钥匙就能做到这一点。
使用键的解决方案链接将按顺序排列:
https://stackoverflow.com/a/28179346/3016153
由于之前已经提出了XSLT 2.0解决方案,我建议另一个我认为更简单的解决方案:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/element">
<xsl:copy>
<xsl:for-each-group select="hello" group-starting-with="hello[.='{%tab%}']">
<xsl:for-each-group select="current-group()" group-ending-with="hello[.='{%endtab%}']">
<xsl:if test="current-group()[self::hello[.='{%tab%}']]">
<xsl:for-each select="current-group()[not(position()=1 or position()=last())]">
<strong><xsl:value-of select="." /></strong>
</xsl:for-each>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:0)
修改:一开始我没有注意到xslt-1.0标记;这个答案对原始海报来说可能毫无用处
使用 XSLT 2.0 和@bjimba's answer相同的聪明技巧,您可以将&#34;标签&#34;中的元素组合在一起,这样可以同时使用整个组和每个hello
元素:
XSLT 2.0
...
<xsl:for-each-group select="hello" group-by="my:tabId(.)">
<!-- do something with a whole run -->
<strong>
<!-- do something with the single elements -->
<xsl:apply-templates select="current-group()"/>
</strong>
</xsl:for-each-group>
...
<xsl:function name="my:tabId" as="xs:integer?">
<xsl:param name="e" as="element()"/>
<xsl:variable name="tabStartCount"
select="count($e/preceding-sibling::hello[. = '{%tab%}'])"/>
<xsl:variable name="tabEndCount"
select="count($e/preceding-sibling::hello[. = '{%endtab%}'])"/>
<xsl:sequence select="
if ($e != '{%endtab%}' and ($tabStartCount > $tabEndCount)) then
$tabStartCount
else
()
"/>
</xsl:function>