我是XSLT上的菜鸟。 我有一个XML,其中t节点后跟其他节点,然后另一个t节点可能再次出现,然后再次出现节点,依此类推
<t />
<n1 />
<n2 />
..
<t/>
<n3 />
<n4 />
...
我需要将此XML转换为HTML,其中t个节点将其后的所有节点包装到下一个t节点
<div class='t'>
<div class='n1'/>
<div class='n2'/>
...
</div>
<div class='t'>
<div class='n3'/>
<div class='n4'/>
...
</div>
我很难实现这一点。 任何想法\提示?
谢谢!
答案 0 :(得分:6)
这是对邻居的分组。有很多解决方案:
确认这个良好的输入:
<root>
<t />
<n1 />
<n2 />
<t/>
<n3 />
<n4 />
</root>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="node()[1]" mode="group"/>
<xsl:apply-templates select="t"/>
</xsl:copy>
</xsl:template>
<xsl:template match="t">
<div class="t">
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</div>
</xsl:template>
<xsl:template match="t" mode="group"/>
<xsl:template match="node()" mode="group">
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:key name="kNodeByMark"
match="node()[../t][not(self::t)]"
use="generate-id((..|preceding-sibling::t[1])[last()])"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="key('kNodeByMark',generate-id())"/>
<xsl:for-each select="t">
<div class="t">
<xsl:apply-templates
select="key('kNodeByMark',generate-id())"/>
</div>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
for-each-group
指令<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="node()[../t[1] >> .]"/>
<xsl:for-each-group select="node()" group-starting-with="t">
<div class="t">
<xsl:apply-templates
select="current-group()[position()>1]"/>
</div>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
输出:
<root>
<div class="t">
<div class="n1" />
<div class="n2" />
</div>
<div class="t">
<div class="n3" />
<div class="n4" />
</div>
</root>
编辑:遍历跟踪轴重构,看起来像其他解决方案。剥离身份规则。
答案 1 :(得分:0)
请参阅我关于您的问题的说明,关于“哪个XSLT版本?”。如果您的目标版本支持分组,请在此处查看其他答案,因为这更容易理解,并且几乎肯定会在任何XSLT处理器上执行得更好。如果您不确定,我建议使用像这样的1.0解决方案。
你可以使用与大多数XSLT处理器一样的“XML片段”来完成它,但我在你的XML中添加了一个“root”元素,以减少回答你问题时的某些未知数。
在下面的解决方案中,我试图在XSLT的形状和您想要的输出形状之间保持直接关联。在我看来,这使得维护/理解更容易,至少对于较小的样式表而言。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<xsl:for-each select="t">
<div class='t'>
<xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]">
<div class='{name()}' />
</xsl:for-each>
</div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
“follow-sibling :: * [count(previous-sibling :: t)=(count(current()/ preceding-sibling :: t)+ 1)的右侧而不是(self :: t)]“可以简化,我敢肯定,使用类似”current():: position()“(这是无效的,fyi),但我生锈了,不记得一些别名语法。
这基本上说:1)评估每一个T. 2)选择前面有相同数量T的元素,作为我们当前评估的T的索引。
请注意,您可能已尝试在程序上进行迭代,并发现无法存储在XSLT中找到的最后一个值。或者您已经发现可以,但只能使用嵌套模板。您正在执行的这种类型的支点有许多XSLT新手遇到障碍,所以不要感觉不好。