我有以下标记,一个包含TR和一个或两个子TD的表格
<table>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
</table>
我正在尝试使用XSLT 1.0将其转换为
<AAA>
XXX
<BBB>YYYYY</BBB>
<BBB>YYYYY</BBB>
...
</AAA>
<AAA>
XXX
<BBB>YYYYY</BBB>
<BBB>YYYYY</BBB>
...
</AAA>
...
从这个事实中抽象出来这可能是在XSLT中接近嵌套循环的最佳方法,XPath表达式放入了什么?下面将选择当前TR(XXX)的所有TR兄弟,它们有两个TD子节点,但只到下一个TR(XXX)。我根据他们有多少TD孩子来区分XXX和YYYYY的节点。 1 = XXX,2 = YYYYY。
<xsl:for-each select="//table/tr">
<xsl:if test="count(td) = '1'">
XXX
</xsl:if>
<xsl:for-each select="???">
all YYYYY up until the next XXX
</xsl:for-each>
</xsl:for-each>
我有“follow-sibling :: tr [child :: td [following-sibling :: td]”但是匹配所有以下YYYYY到最后 - 我如何让它只选择那些直到下一个TR与XXX?
谢谢!
答案 0 :(得分:1)
<强>予。 XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kFollowing" match="tr[td[2]]"
use="generate-id(preceding-sibling::tr
[not(td[2])]
[1]
)"/>
<xsl:template match="/*">
<xsl:apply-templates select="tr[not(td[2])]"/>
</xsl:template>
<xsl:template match="tr[not(td[2])]">
<AAA>
<xsl:value-of select="concat('
', td/p, '
')"/>
<xsl:apply-templates select="key('kFollowing', generate-id())"/>
</AAA>
</xsl:template>
<xsl:template match="p">
<BBB><xsl:value-of select="."/></BBB>
</xsl:template>
</xsl:stylesheet>
在提供的XML文档上应用此XSLT 1.0转换:
<table>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>XXX</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
<tr>
<td>
<p>YYYYYY</p>
</td>
<td>
<p>YYYYYY</p>
</td>
</tr>
</table>
生成想要的正确结果:
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<强>解释强>:
正确使用 key 来定义具有第二个tr
子项的所有td
作为 generate-id()
的函数前一个单子tr
元素。
<强> II。 XSLT 2.0解决方案:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:for-each-group group-starting-with="tr[not(td[2])]"
select="tr">
<xsl:apply-templates select="current-group()[1]"/>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="tr[not(td[2])]">
<AAA>
<xsl:value-of select="concat('
', td/p, '
')"/>
<xsl:apply-templates select="current-group()[position() gt 1]"/>
</AAA>
</xsl:template>
<xsl:template match="p">
<BBB><xsl:value-of select="."/></BBB>
</xsl:template>
</xsl:stylesheet>
当此转换应用于同一XML文档(上图)时,会产生相同的正确结果:
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
<BBB>YYYYYY</BBB>
</AAA>
<强>解释强>:
适当使用带有group-starting-with
属性的 xsl:for-each-group
和 current-group()
功能。
答案 1 :(得分:0)
我一直认为让XPath结构完全符合你的要求是有用的:序列中的所有项目(包括/排除)匹配某些条件的第一项。我已经考虑过这样的语法,例如expr until condition
。 Saxon和EXSLT尝试提供更高阶的扩展函数来满足要求,例如saxon:leading()。可悲的是,规范中没有任何内容;虽然在XPath 3.0中,您可以使用高阶函数轻松完成。
您有时可以使用分组来解决问题(xsl:for-each-group),但通常最简单和最优雅的方法是使用递归:即编写一个处理序列中第一个项目的函数,如果某些条件成立,则调用自己处理下一个项目。
另一种方法是在序列中搜索与条件匹配的第一个节点,然后使用subsequence()选择到该位置的节点:
<xsl:variable name="positions" select="
for $i in 1 to count($seq)
return if (my:condition($seq[$i])) then $i else ()"/>
<xsl:apply-templates select="subsequence($seq, 1, $positions[1])"/>