-1

需要从列表中选择四种类型的数据并分组在一个表中。XML 输入的示例如下所示:

<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>fr.0.7.1 : en1.5.1</li>
    <li>        : en1.6.1</li>
  </ul
</DIV>

每个具有相同前缀(例如'fr')的3位数字中的第2位相对于前后3位数字的第2位,例如fr0.1.1,fr0.2.1,fr0.3.1是连续的在这种情况下,fr0.1.1、fr0.3.1、fr0.2.1 OR fr0.1.1、fr0.3.1 是不连续的。

我需要一个代码,它只能在每个 <li> 元素左侧以“fr”为前缀的数字中发现这些关系中的差距和差异。如果这样的序列中缺少一个数字,代码需要检索它并将其列在名为“Absent(fr)”的第一列中。如果第二个数字没有按增长顺序排列,因为你在左边所有 3 位数字的列表中向下,代码应该识别第 2 位数字在增长顺序中存在差异的数字,例如它是 fr0 .6.1 在上面的输入示例中为 (0.)4(.1)<(0.)6(.1)>(0.)5(.1)。如果找到这样的数字,它应该列在名为“不符”的第二列中。第三列应在其右侧列出与差异数字匹配的数字。

名为“No-match(en)”的第四列应该列出以“en”为前缀的数字,前提是它们的右侧没有以“fr”为前缀的匹配项。在示例中,这样的数字是 1.6.1。

第五列“重复”应列出以“en”为前缀的数字,前提是它在输入列表中出现多次。并且代码应检索以“fr”为前缀的重复编号的匹配项,并将其列在最后一列“匹配项”中。

HTML 输出中的表格应如下所示(颜色或粗体等样式并不重要!):

桌子

xml 输出将如下所示:

<table>
 <tr>   
   <th>Absent(en)</th>
   <th>Discrepant</th>
   <th>Match</th>
   <th>No-match(en)</th>
   <th>Repetition</th>
  <th>Matches</th>
 </tr>
 <tr>
   <td>0.3.1</td>
   <td>0.6.1</td>
   <td>1.4.1</td>
   <td>1.6.1</td>
   <td>1.5.1</td>
   <td>0.5.1, 0.7.1</td>
 </tr>
</table>
4

1 回答 1

0

我不会回答你的整个问题。对于 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 的好书并阅读它。如果您选择正确的书,它实际上是一种非常容易学习的语言。这就是我所做的,我发现我的投资回报是充足的。

于 2013-07-22T13:34:02.437 回答