我不会回答你的整个问题。对于 StackOverflow 来说有点过分了。我要做的是回答您问题的核心部分(第一列)。这应该足以推断得到完整的答案。如果您从我的回答中推断出问题,那么将问题的未解决部分细分为较小的任务,并在每个任务上发布一个单独的 SO 问题,当然,在您自己进行了很好的尝试之后。
这个输入文件...(与你的略有不同,纠正格式错误,不处理最后一个li的情况)
<DIV>
<ul>
<li>fr0.1.1 : en1.1.1</li>
<li>fr0.2.1 : en1.2.1</li>
<li>fr0.4.1 : en1.3.1</li>
<li>fr0.6.1 : en1.4.1</li>
<li>fr0.5.1 : en1.5.1</li>
<li>fr0.7.1 : en1.5.1</li>
</ul>
</DIV>
...当应用于此 XSLT 2.0 样式表时...
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:so="http://stackoverflow.com/questions/17776650"
exclude-result-prefixes="xsl xs fn so">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:function name="so:middle-number" as="xs:integer">
<xsl:param name="dotted-text" as="xs:string" />
<xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast as xs:integer" />
</xsl:function>
<xsl:function name="so:delta" as="xs:integer">
<!-- Difference between this node and the previous.
Count the first node as having difference = 1 . -->
<xsl:param name="li" as="element()" />
<xsl:sequence select="
if ($li/preceding-sibling::li) then
so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())
else
1" />
</xsl:function>
<xsl:function name="so:in-between" as="xs:integer*">
<xsl:param name="lower-bound" as="xs:integer" />
<xsl:param name="upper-bound" as="xs:integer" />
<xsl:variable name="diff" select="$upper-bound - $lower-bound" />
<xsl:choose>
<xsl:when test="$diff eq 0">
<xsl:sequence select="$lower-bound" />
</xsl:when>
<xsl:when test="$diff eq 1">
<xsl:sequence select="$lower-bound, $lower-bound+1" />
</xsl:when>
<xsl:when test="($diff ge 2)">
<xsl:variable name="half-way" select="fn:round( $lower-bound + ($diff div 2))" />
<xsl:sequence select="so:in-between($lower-bound, $half-way) , so:in-between( $half-way+1, $upper-bound)" />
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:function>
<xsl:template match="/*/ul">
<xsl:variable name="groups">
<xsl:for-each-group select="li" group-adjacent="so:delta(.)">
<so:group>
<so:start><xsl:value-of select="so:middle-number( current-group()[1 ]/text())" /></so:start>
<so:end> <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>
</so:group>
</xsl:for-each-group>
</xsl:variable>
<xsl:apply-templates select="$groups" />
</xsl:template>
<xsl:template match="so:group[1]" />
<xsl:template match="so:group">
<xsl:variable name="this" select="so:start/text() cast as xs:integer" as="xs:integer" />
<xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast as xs:integer" as="xs:integer" />
<xsl:for-each select="for $x in so:in-between( $prev + 1, $this - 1) return $x">
<td><xsl:value-of select="concat('0.',.,'.1')" /></td>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
...产生结果...
<td>0.3.1</td>
<td>0.6.1</td>
..这是两个不存在的 li 值。
带回家点
您可以使用 xsl:for-each-group/@group-adjacent 指令,对一个 li 节点与其前一个节点的值之间的增量进行分组。这为您提供了一组顺序节点。因此,只需跨越一组结束和下一组开始之间的差异,您就会拥有所有缺失的值。这为您提供了第一列。
对其他列使用类似的技术。
更新
我刚刚意识到您不需要 so:inbetween() 函数。您可以简单地使用to
运算符。对于那个很抱歉。更改为to
运算符应该会大大简化样式表。
更新 2
这是to
版本......我认为它是令人满意的小!
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:so="http://stackoverflow.com/questions/17776650"
exclude-result-prefixes="xsl xs fn so">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:function name="so:middle-number" as="xs:integer">
<xsl:param name="dotted-text" as="xs:string" />
<xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast as xs:integer" />
</xsl:function>
<xsl:function name="so:delta" as="xs:integer">
<!-- Difference between this node and the previous.
Count the first node as having difference = 1 . -->
<xsl:param name="li" as="element()" />
<xsl:sequence select="
if ($li/preceding-sibling::li) then
so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())
else
1" />
</xsl:function>
<xsl:template match="/*/ul">
<xsl:variable name="groups">
<xsl:for-each-group select="li" group-adjacent="so:delta(.)">
<so:group>
<so:start><xsl:value-of select="so:middle-number( current-group()[1 ]/text())" /></so:start>
<so:end> <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>
</so:group>
</xsl:for-each-group>
</xsl:variable>
<xsl:apply-templates select="$groups" />
</xsl:template>
<xsl:template match="so:group[1]" />
<xsl:template match="so:group">
<xsl:variable name="this" select="so:start/text() cast as xs:integer" as="xs:integer" />
<xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast as xs:integer" as="xs:integer" />
<xsl:for-each select="for $x in $prev + 1 to $this - 1 return $x">
<td><xsl:value-of select="concat('0.',.,'.1')" /></td>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
关于 XSLT 太难了
关于您关于 XSLT 太多而无法理解的评论:请记住,XSLT 就像任何其他语言一样。你不能指望仅仅通过在 StackOverflow 上发布一两个问题来学习一门语言。我怀疑从长远来看,投资最能满足您的利益。拿起一本关于 XSLT 2 的好书并阅读它。如果您选择正确的书,它实际上是一种非常容易学习的语言。这就是我所做的,我发现我的投资回报是充足的。