我通过以下方式构建XML:
<content>
<sh1>A</sh1>
<sh2>A1</sh2>
<sh2>A2</sh2>
<sh1>B</sh1>
<sh2>B1</sh2>
<sh2>B2</sh2>
<sh2>B3</sh2>
<sh1>C</sh1>
<sh2>C1</sh2>
<sh2>C2</sh2>
<sh1>D</sh1>
<sh2>D1</sh2>
<sh2>D2</sh2>
<sh2>D3</sh2>
<sh2>D4</sh2>
</content>
正如您所看到的,这里有两个关注的标记名称:sh1
和sh2
。这些表示标题和子标题,我需要通过嵌套它们的内容在输出XML中组织它们(您可以看到示例输出here)。到目前为止,我的方法是匹配每个sh1
,然后尝试匹配它与下一个sh2
之间的每个sh1
。
我发现了this超级有用的问题,它让我(我希望)大部分都在这里。我把以下的XSLT(1.0)放在一起:
<xsl:template match="content">
<ul>
<xsl:apply-templates select="//sh1" />
</ul>
</xsl:template>
<xsl:template match="sh1">
<li>
<h1><xsl:value-of select="." /></h1>
<ul>
<xsl:apply-templates select="./following-sibling::sh1[1]/preceding-sibling::sh2[preceding-sibling::.]" />
</ul>
</li>
</xsl:template>
<xsl:template match="sh2">
<li><xsl:value-of select="." /></li>
</xsl:template>
问题(可预测)是我不能使用./following-sibling::sh1[1]/preceding-sibling::sh2[preceding-sibling::.]
使用.
表示sh2
应该将当前节点作为前一个兄弟。我可以使用其他一些关键字吗?或者或许另一种方法完全是为了得到我想要的嵌套结构?
答案 0 :(得分:2)
我相信使用键是最方便的方法:
XSLT 1.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="sh2" match="sh2" use="generate-id(preceding-sibling::sh1[1])" />
<xsl:template match="/content">
<ul>
<xsl:apply-templates select="sh1" />
</ul>
</xsl:template>
<xsl:template match="sh1">
<li>
<h1><xsl:value-of select="." /></h1>
<ul>
<xsl:apply-templates select="key('sh2', generate-id())" />
</ul>
</li>
</xsl:template>
<xsl:template match="sh2">
<li><xsl:value-of select="." /></li>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
在XSLT 2.0中,我会使用for-each-group
来解决问题:
<xsl:template match="content">
<ul>
<xsl:for-each-group select="*" group-starting-with="sh1">
<li>
<h1><xsl:value-of select="." /></h1>
<xsl:if test="current-group()[2]">
<ul>
<xsl:apply-templates select="current-group() except ." />
</ul>
</xsl:if>
</li>
</xsl:for-each-group>
</ul>
</xsl:template>
<xsl:template match="sh2">
<li><xsl:value-of select="." /></li>
</xsl:template>
如果您被限制为1.0,那么基于密钥的方法as suggested by michael.hor257k可能是最有效的,但对现有方法的最简单修复就像
<xsl:apply-templates select="following-sibling::sh2[
generate-id(preceding-sibling::sh1[1]) = generate-id(current())]" />
preceding-sibling::sh1[1]
是我们当时正在过滤的节点的前一个sh1
,current()
是模板正在运行的sh1
元素。比较它们的generate-id
值是XSLT 1.0方法来确定两个表达式是否选择相同的节点(如果你只是比较了值而不是它们生成的ID,那么你就会将误报作为两个不同的风险具有相同文本的sh1
个元素将被视为相等。在2.0中,您可以使用is
运算符执行此操作(preceding-sibling::sh1[1] is current()
)。
或者,您可以使用我所听到的称为“兄弟递归”的技术来模拟“while循环”:将模板应用于此标题下的第一个sh2
,并将该模板递归到下一个兄弟,依此类推,直到我们用完了相应的节点:
<ul>
<xsl:apply-templates select="following-sibling::*[1][self::sh2]" />
</ul>
和模板:
<xsl:template match="sh2">
<li><xsl:value-of select="." /></li>
<xsl:apply-templates select="following-sibling::*[1][self::sh2]" />
</xsl:template>
只要following-sibling::*[1]
不是sh2
元素,“循环”就会终止。