与此问题类似(有更多相关条目,但作为新用户,我只能发布一个网址): Xpath Get elements that are between 2 elements
我有一个关于“其他/分隔”元素之间选择的元素集的问题。尝试将平面HTML表转换为时会出现这种情况 使用XSLT的分层XML结构。我尝试在模板中使用递归,但是撒克逊拒绝接受这个,因为它导致死锁,很可能是我的错,但让我们从头开始。
首先,源数据是HTML表格:
<table >
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr>
<th colspan="3" >Group 1</th>
</tr>
<tr>
<td>attribute 1.1.1</td>
<td>attribute 1.1.3</td>
<td>attribute 1.1.2</td>
</tr>
<tr>
<td>attribute 1.2.1</td>
<td>attribute 1.2.2</td>
<td>attribute 1.2.3</td>
</tr>
<tr>
<td>attribute 1.3.1</td>
<td>attribute 1.3.2</td>
<td>attribute 1.3.3</td>
</tr>
<tr>
<th colspan="3" >Group 2</th>
</tr>
<tr>
<td>attribute 2.1.1</td>
<td>attribute 2.1.3</td>
<td>attribute 2.1.2</td>
</tr>
<tr>
<td>attribute 2.2.1</td>
<td>attribute 2.2.2</td>
<td>attribute 2.2.3</td>
</tr>
<tr>
<td>attribute 2.3.1</td>
<td>attribute 2.3.2</td>
<td>attribute 2.3.3</td>
</tr>
</tbody>
</table>
XML中的目标输出是:
<groups>
<group name="Group 1">
<item attribute1="attribute 1.1.1" attribute2="attribute 1.1.3" attribute3="attribute 1.1.2"/>
<item attribute1="attribute 1.2.1" attribute2="attribute 1.2.2" attribute3="attribute 1.2.3"/>
<item attribute1="attribute 1.3.1" attribute2="attribute 1.3.2" attribute3="attribute 1.3.3"/>
</group>
<group name="Group 2">
<item attribute1="attribute 2.1.1" attribute2="attribute 2.1.3" attribute3="attribute 2.1.2"/>
<item attribute1="attribute 2.2.1" attribute2="attribute 2.2.2" attribute3="attribute 2.2.3"/>
<item attribute1="attribute 2.3.1" attribute2="attribute 2.3.2" attribute3="attribute 2.3.3"/>
</group>
</groups>
所以我希望拥有所有项目条目(TR元素)并将它们添加到一个组中。这基本上归结为选择所有后续兄弟TR元素,直到遇到具有TH元素作为子元素的元素。如果我只能确定具有TH子节点的第一个TR的位置,指示组的新标题,则可以通过以下方式完成:
<xsl:for-each select="tbody/tr">
<xsl:if test="th">
<xsl:element name="group">
<xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
<xsl:for-each select="following-sibling::tr[position() < $positionOfNextThElement]">
<xsl:call-template name="item"/>
</xsl:for-each>
</xsl:element>
</xsl:if>
</xsl:for-each>
但是,我无法确定第一个遇到的TR / TH标签的位置。
如上所述,我尝试在模板中使用递归:始终调用“item”模板,并在此模板中确定是否要在下一个项目上调用它。我认为问题在于从模板中调用模板。上下文中的项目没有增加?我应该移交一个参数来确定我们正在处理的项目吗?
无论如何,这就是我想出来的:
<xsl:for-each select="tbody/tr">
<xsl:if test="th">
<xsl:element name="group">
<xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
<xsl:call-template name="item"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
<xsl:template name="item">
<xsl:element name="item">
<xsl:attribute name="attribute1"><xsl:value-of select="following-sibling::tr[1]/td[1]"/></xsl:attribute>
<xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[2]"/></xsl:attribute>
<xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[3]"/></xsl:attribute>
</xsl:element>
<!-- When the next element has not got a TH tag, continue with invoking this template -->
<xsl:if test="count(following-sibling::tr[1]/th) != 1">
<xsl:call-template name="item"/>
</xsl:if>
</xsl:template>
欢迎任何关于如何实现这一点的建议!
答案 0 :(得分:1)
当递归调用模板“item”时上下文不会增加的原因是xs:call-template总是将当前上下文项作为上下文传递。正如您可能看到的那样,转换只会进入无限递归。
假设您总是需要生成三个属性,您甚至不需要递归。
试试这个:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="table">
<groups>
<xsl:apply-templates select="tbody/tr[th]"/>
</groups>
</xsl:template>
<xsl:template match="tr[th]">
<xsl:variable name="id" select="generate-id(.)"/>
<group name="{string(th)}">
<xsl:apply-templates
select="following-sibling::tr[not(th)][generate-id(preceding-sibling::tr[th][1]) = $id]"/>
</group>
</xsl:template>
<xsl:template match="tr">
<item attribute1="{td[1]}" attribute2="{td[2]}" attribute3="{td[3]}" />
</xsl:template>
</xsl:stylesheet>
这适用于将模板应用于每个标题行。这些模板中的每一个都使用那个复杂的xpath来调用“它的”跟随行,这些行是具有该特定行的任何后续兄弟行,因为它是带有标题的第一行。
当然,如果属性数量不同,那么您需要递归到那里并增加传递指示位置的参数。
有一些已建立的XSLT分组方法,其中一种是递归的,就像你正在做的那样。另一种方法称为Muenchian分组。好的文章是here。
答案 1 :(得分:0)
另一种解决方案,适用于不带递归的变量属性计数。
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="table">
<groups>
<xsl:apply-templates select="tbody/tr[th]"/>
</groups>
</xsl:template>
<xsl:template match="tr[th]">
<group name="{th}">
<xsl:apply-templates select="
following-sibling::tr[not(th)][
generate-id(preceding-sibling::tr[th][1]) = generate-id(current())
]
"/>
</group>
</xsl:template>
<xsl:template match="tr">
<item>
<xsl:apply-templates select="td" />
</item>
</xsl:template>
<xsl:template match="td">
<xsl:attribute name="attribute{position()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>