1

我有以下xml:

输入

<page>
 <group category="cat1">
  <item fileunder="#">.45 colt</item>
  <item fileunder="#">8 queens</item>
  <item fileunder="#">9 lives</item>
  <item fileunder="#">99 bottles of beer</item>
  <item fileunder="A">An innocent man</item>
  <item fileunder="A">Academy awards</item>
  <item fileunder="B">Before the dawn</item>
 </group>
 <group category="cat2">
  <item fileunder="R">Rows of houses</item>
 </group>
</page>

输入项已经排序。

期望的输出

我想为 each 生成一个 3 列 HTML 表格,每个group不同的 都有一个副标题(一个 3 列跨越单元格)fileunder,最佳地呈现在自上而下的 then-next-column 中(项目已经排序):

<table>
 <tr><td colspan="3">#</td></tr>
 <tr><td>.45 colt</td><td>9 lives</td><td>99 bottles of beer</td></tr>
 <tr><td>8 queens</td></tr>
 <tr><td colspan="3">A</td></tr>
 <tr><td>An innocent man</td><td>Academy awards</td></tr>
 <tr><td colspan="3">B</td></tr>
 <tr><td>Before the dawn</td></tr>
</table>
<table>
 <tr><td colspan="3">R</td></tr>
 <tr><td>Rows of houses</td></tr>
</table>

如果项目以从左到右,然后是下一行的形式呈现,我可以活下去。

到目前为止,我所拥有的是:

当前 xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="itm_grp" match="/page/group/item" use="concat(../@category,':',@fileunder)"/>
<xsl:template match="page/group">
  <table>
  <xsl:for-each select="item[.=key('itm_grp',concat(../@category,':',@fileunder))[1]]">
    <tr><td colspan="3"><xsl:value-of select="@fileunder"/></td></tr>
    <xsl:variable name="nodeset" select="key('itm_grp',concat(../@category,':',@fileunder))"/>
    <xsl:for-each select="$nodeset[position() mod 3=1]">
      <tr>
      <td><xsl:value-of select="."/></td>
      <td><xsl:value-of select="following-sibling::item[1]"/></td>
      <td><xsl:value-of select="following-sibling::item[2]"/></td>
      </tr>
    </xsl:for-each>
  </xsl:for-each>
  </table>
</xsl:template>
</xsl:stylesheet>

它产生从左到右,然后是下一行的输出(非最佳);但是,following-sibling选择会产生“渗透”效果:

#
.45 colt            8 queens         9 lives
99 bottles of beer  An innocent man  Academy awards
A
An innocent man     Academy awards   Before the dawn
B
Before the dawn     
R
Rows of houses      

如您所见,fileunder #有两个 A 项,fileunder A一个 B 项。

所以,我的问题是:

如何产生所需的输出(按列)?
如果我不能这样做,我怎样才能让逐行输出避免“出血”?

请注意,我对 XSLT 的经验很少,所以如果我的代码公然低效/愚蠢/无论如何,请随时通过替换所有代码来教育我!

注意:XSLT 版本 1,因此显然没有index-of可用的功能。

4

2 回答 2

1

解决这个问题的最简单方法:

<xsl:variable name="header" select="@fileunder"/>
...
<xsl:value-of select="following-sibling::item[@fileunder=$header][1]"/>
<xsl:value-of select="following-sibling::item[@fileunder=$header][2]"/>
于 2012-11-13T09:59:35.903 回答
1

您的叙述与您列出的预期输出之间存在轻微的矛盾。您已要求自上而下,然后是左右列的填充顺序,您在列表中对非空值有这样的要求,但对于空值则没有。这种空间顺序意味着必须先填写一整列,然后才能开始下一列。我假设您的列表是一个错误,而您真正想要的输出是......

<table>
  <tr>
    <td colspan="3">#</td>
  </tr>
  <tr>
    <td>.45 colt</td>
    <td>9 lives</td>
    <td>&npsp;</td>
  </tr>
  <tr>
    <td>8 queens</td>
    <td>99 bottles of beer</td>
    <td>&npsp;</td>
  </tr>
  <tr>
    <td colspan="3">A</td>
  </tr>
  <tr>
    <td>An innocent man</td>
    <td>Academy awards</td>
    <td>&npsp;</td>
  </tr>
  <tr>
    <td colspan="3">B</td>
  </tr>
  <tr>
    <td>Before the dawn</td>
    <td>&npsp;</td>
    <td>&npsp;</td>
  </tr>
</table>
<table>
  <tr>
    <td colspan="3">R</td>
  </tr>
  <tr>
    <td>Rows of houses</td>
    <td>&npsp;</td>
    <td>&npsp;</td>
  </tr>
</table>

...这是一致的自上而下,然后是左右列填充顺序。

这个 XSLT 1.0 样式表...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />  

<xsl:key name="kItemByFile" match="item" use="concat(../@category,':',@fileunder)"/>

<xsl:template match="/">
 <html lang="en">
   <head><title>Songs</title></head>
   <body>  
     <xsl:apply-templates select="*/group" />
   </body>  
 </html>
</xsl:template>

<xsl:template match="group">
  <xsl:variable name="cat" select="concat(@category,':')" />
  <table> 
    <xsl:apply-templates select="item[
      generate-id() = generate-id(key('kItemByFile',concat($cat,@fileunder))[1])]" 
      mode="group-head" />
  </table> 
</xsl:template>

<xsl:template match="item" mode="group-head">
  <xsl:variable name="items"
     select="key('kItemByFile',concat(../@category,':',@fileunder))" />
  <xsl:variable name="row-count" select="ceiling( count($items) div 3)" />
  <tr><td colspan="3"><xsl:value-of select="@fileunder" /></td></tr>
  <xsl:for-each select="$items[position() &lt;= $row-count]">
    <xsl:variable name="pos" select="position()" />
    <xsl:apply-templates select="." mode="row">  
      <xsl:with-param name="items" select="$items" />
      <xsl:with-param name="row" select="$pos" />
      <xsl:with-param name="row-count" select="$row-count" />
    </xsl:apply-templates>  
  </xsl:for-each>
</xsl:template>

<xsl:template match="item" mode="row">
  <xsl:param name="items" select="/.." />
  <xsl:param name="row" select="1" />
  <xsl:param name="row-count" select="1" />
  <tr>
    <xsl:apply-templates select="
       $items[(position() mod $row-count) = ($row mod $row-count)]" mode="td" />
    <xsl:variable name="full-cols" select="floor((count($items) div $row-count))" />
    <xsl:variable name="part-col" select="number($row &lt;
                 ((count($items) mod $row-count) + 1))" />
    <xsl:variable name="empties" select="3 - ($full-cols + $part-col)" />
    <xsl:for-each select="(document('')/*/*)[position() &lt;= $empties]">
      <xsl:call-template name="empty-cell" />
    </xsl:for-each> 
  </tr>  
</xsl:template>

<xsl:template match="item" mode="td">
  <td><xsl:value-of select="." /></td>  
</xsl:template>

<xsl:template name="empty-cell">
  <td>&#160;</td>
</xsl:template>

</xsl:stylesheet>

...当应用于此输入时...

<page>
 <group category="cat1">
  <item fileunder="#">.45 colt</item>
  <item fileunder="#">8 queens</item>
  <item fileunder="#">9 lives</item>
  <item fileunder="#">99 bottles of beer</item>
  <item fileunder="A">An innocent man</item>
  <item fileunder="A">Academy awards</item>
  <item fileunder="B">Before the dawn</item>
 </group>
 <group category="cat2">
  <item fileunder="R">Rows of houses</item>
 </group>
</page>

...产量...

<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en">
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Songs</title>
  </head>
  <body>
    <table>
      <tr>
        <td colspan="3">#</td>
      </tr>
      <tr>
        <td>.45 colt</td>
        <td>9 lives</td>
        <td> </td>
      </tr>
      <tr>
        <td>8 queens</td>
        <td>99 bottles of beer</td>
        <td> </td>
      </tr>
      <tr>
        <td colspan="3">A</td>
      </tr>
      <tr>
        <td>An innocent man</td>
        <td>Academy awards</td>
        <td> </td>
      </tr>
      <tr>
        <td colspan="3">B</td>
      </tr>
      <tr>
        <td>Before the dawn</td>
        <td> </td>
        <td> </td>
      </tr>
    </table>
    <table>
      <tr>
        <td colspan="3">R</td>
      </tr>
      <tr>
        <td>Rows of houses</td>
        <td> </td>
        <td> </td>
      </tr>
    </table>
  </body>
</html>

注意 对于输出中的空单元格,在查看词法 HTML 时,您将获得&nbsp;或得到等效的文字空白。它依赖于 XSLT 处理器实现,但不应引起您的任何担忧,因为它是模型等效的。

于 2012-11-13T13:12:00.027 回答