如何从XML中的2级分组生成列式输出?

时间:2012-11-13 09:40:39

标签: xslt grouping

我有以下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>

输入项已经排序。

期望的输出

我想为每个group生成一个3列HTML表格,每个不同的fileunder都有一个子标题(一个3列生成单元格),最佳显示在自上而下,然后-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功能可用。

2 个答案:

答案 0 :(得分: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]"/>

答案 1 :(得分: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处理器实现,但不应引起任何顾虑,因为它与模型等效。