如何将排序列表转换为表,每个单元格具有多个值

时间:2018-02-09 15:54:26

标签: xml xslt

我有一个项目列表,已排序 我希望将它们显示在一个包含4列和多行的表中(取决于列表的大小,可能会有所不同)。

表中的每个单元格都填充了9个条目。第一个单元格应包含9个第一个条目,第二个单元格应包含从10到18开始的条目,第三个单元格从19到27,第四个单元格从28到36完成第一行。

第2行的第一个单元格应以条目37到45开头,依此类推......

为此,我使用for-each指令

创建了一个XSLT文件
<xsl:for-each select="catalog/cd[position() &lt; '10']"> 

我检查了position()参数。

在第二个单元格中,我使用语句:

<xsl:for-each select="catalog/cd[position() &gt; '9' and position() &lt; '19']">

但它根本不起作用,尽管这对我来说似乎是合乎逻辑的。 我是XML和XSLT的新手,所以如果有人可以帮助我吗?

我使用了W3schools的例子。

所以这是我的XML(标准示例)

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
    <cd>
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your heart</title>
        <artist>Bonnie Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title>Greatest Hits</title>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <company>RCA</company>
        <price>9.90</price>
        <year>1982</year>
    </cd>
    <cd>
        <title>Still got the blues</title>
        <artist>Gary Moore</artist>
        <country>UK</country>
        <company>Virgin records</company>
        <price>10.20</price>
        <year>1990</year>
    </cd>
    <cd>
        <title>Eros</title>
        <artist>Eros Ramazzotti</artist>
        <country>EU</country>
        <company>BMG</company>
        <price>9.90</price>
        <year>1997</year>
    </cd>
    <cd>
        <title>One night only</title>
        <artist>Bee Gees</artist>
        <country>UK</country>
        <company>Polydor</company>
        <price>10.90</price>
        <year>1998</year>
    </cd>
    <cd>
        <title>Sylvias Mother</title>
        <artist>Dr.Hook</artist>
        <country>UK</country>
        <company>CBS</company>
        <price>8.10</price>
        <year>1973</year>
    </cd>
    <cd>
        <title>Maggie May</title>
        <artist>Rod Stewart</artist>
        <country>UK</country>
        <company>Pickwick</company>
        <price>8.50</price>
        <year>1990</year>
    </cd>
    <cd>
        <title>Romanza</title>
        <artist>Andrea Bocelli</artist>
        <country>EU</country>
        <company>Polydor</company>
        <price>10.80</price>
        <year>1996</year>
    </cd>
    <cd>
        <title>When a man loves a woman</title>
        <artist>Percy Sledge</artist>
        <country>USA</country>
        <company>Atlantic</company>
        <price>8.70</price>
        <year>1987</year>
    </cd>
    <cd>
        <title>Black angel</title>
        <artist>Savage Rose</artist>
        <country>EU</country>
        <company>Mega</company>
        <price>10.90</price>
        <year>1995</year>
    </cd>
    <cd>
        <title>1999 Grammy Nominees</title>
        <artist>Many</artist>
        <country>USA</country>
        <company>Grammy</company>
        <price>10.20</price>
        <year>1999</year>
    </cd>
    <cd>
        <title>For the good times</title>
        <artist>Kenny Rogers</artist>
        <country>UK</country>
        <company>Mucik Master</company>
        <price>8.70</price>
        <year>1995</year>
    </cd>
    <cd>
        <title>Big Willie style</title>
        <artist>Will Smith</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>9.90</price>
        <year>1997</year>
    </cd>
    <cd>
        <title>Tupelo Honey</title>
        <artist>Van Morrison</artist>
        <country>UK</country>
        <company>Polydor</company>
        <price>8.20</price>
        <year>1971</year>
    </cd>
    <cd>
        <title>Soulsville</title>
        <artist>Jorn Hoel</artist>
        <country>Norway</country>
        <company>WEA</company>
        <price>7.90</price>
        <year>1996</year>
    </cd>
    <cd>
        <title>The very best of</title>
        <artist>Cat Stevens</artist>
        <country>UK</country>
        <company>Island</company>
        <price>8.90</price>
        <year>1990</year>
    </cd>
    <cd>
        <title>Stop</title>
        <artist>Sam Brown</artist>
        <country>UK</country>
        <company>A and M</company>
        <price>8.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title>Bridge of Spies</title>
        <artist>T`Pau</artist>
        <country>UK</country>
        <company>Siren</company>
        <price>7.90</price>
        <year>1987</year>
    </cd>
</catalog>

我将我的XSLT缩减为仅对前两列进行测试:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
        <html>
            <body>
                <table border="1">
                    <tr bgcolor="#D7BDE2">
                        <th>Blok1</th>
                        <th>Blok2</th>
                        <th>Blok3</th>
                        <th>Blok4</th>
                    </tr>
                    <td>
                        <table border="1">
                            <tr bgcolor="#9acd32">
                                <th>Title1</th>
                                <th>Artist</th>
                            </tr>
                            <xsl:for-each select="catalog/cd[position() &lt; '10']">
                                <tr>
                                    <td>
                                        <xsl:value-of select="title"/>
                                    </td>
                                    <td>
                                        <xsl:value-of select="artist"/>
                                    </td>
                                </tr>
                            </xsl:for-each>
                        </table>
                    </td>
                    <td>
                        <table border="1">
                            <tr bgcolor="#9acd32">
                                <th>Title2</th>
                                <th>Artist</th>
                            </tr>                            
                            <xsl:for-each select="catalog/cd[
   position() &gt; '9' AND position() &lt; '19' ]">
                                <tr>
                                    <td>
                                        <xsl:value-of select="title"/>
                                    </td>
                                    <td>
                                        <xsl:value-of select="artist"/>
                                    </td>
                                </tr>
                            </xsl:for-each>
                        </table>
                    </td>
                </table>
            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>

非常感谢你帮助我。

PS输出应该看起来像这样(见图)。第一列中的前9张CD,下一列中的以下9张,依此类推。 enter image description here

2 个答案:

答案 0 :(得分:1)

xsl:for-each的使用对我来说总是有一点代码味道。它有它的用途,我想,但我通常倾向于选择xsl:apply-templates和一个单独的顶级模板。在几个地方你有高度重复的代码的事实有更糟糕的代码味道:这只是乞求合并到一个共同的模板。我还观察到你正在尝试的方法是基于能够为每组九张CD写一个单独的for-each,但这不能扩展。

有多种方法可以解决这个问题,但我将建议基于将模板与各种元素分组对齐,并使用模式进行区分。首先是最高级别:

<xsl:template match="/catalog">
  <html>
    <body>
      <table>
        <tr><th>Block1</th><th>Block2</th><th>Block3</th><th>Block4</th></tr>
        <xsl:apply-templates
            select="cd[floor((position() - 1) div 36) = ((position() - 1) div 36)]"
            mode="row"/>
      </table>
    </body>
  </html>
</xsl:template>

设置外部框架,然后,在外部&lt; table&gt;内,它选择每组36 cd中的第一个用于在模式“行”中进行转换。这些是每行第一个块的第一个元素,用作参考点。行转换如下所示:

<xsl:template match="cd" mode="row">
  <tr><xsl:apply-templates
      select="self::node()|following-sibling::cd[position() = 9 or position() = 18 or position = 27]"
      mode="block"/></tr>
</xsl:template>

这很简单。它只是创建了所需的&lt; tr&gt;对于每个顶级行,然后根据模式“块”转换上下文节点及其兄弟9,18和27元素。这是与最后一个类似的交易:选择用于转换的元素是同一行中每个块的第一个,它们将作为其块的参考点。块转换沿着类似的路线:

<xsl:template match="cd" mode="block">
  <td><table>
    <tr><th>Title</th><th>Artist</th></tr>
    <xsl:apply-templates
        select="self::node()|following-sibling::cd[position() &lt; 9]"
        mode="album"/>
  </table></td>
</xsl:template>

它有更多内容,因为我们需要设置内部每块表,但我们再次使用以下兄弟轴来选择上下文节点的对等体进行转换,这次是同一块中的其他节点。最终模板显示单个CD数据:

<xsl:template match="cd" mode="album">
  <tr><td><xsl:value-of select="title"/></td><td><xsl:value-of select="artist"/></td></tr>
</xsl:template>

最后几行与您使用的相同,只是采用不同的方式打包。

请注意,这会发出结构良好的HTML(你的版本缺少一些&lt; tr&gt; s,虽然它很复杂,很容易错过),而且它很好且模块化。它不包括任何样式,因此,我的浏览器对它的再现与罪恶一样丑陋,但我建议通过添加适当的CSS,甚至是外部CSS样式表来纠正它。

答案 1 :(得分:0)

您可以使用以下模板实现9x4解决方案。有两个变量包含根级别的维度。它的模板使用mode属性来区分两个维度,因此如果您想要添加更多维度(超过2个),则必须引入更多mode s。

以下模板的构建如下:

  1. 使用属性apply-templates
  2. 的前36个($vertical*$horizontal) cd元素执行mode="hor"
  3. 在以下元素周围添加<tr>元素,并再次使用前{36} apply-templates ($vertical*$horizontal)元素添加cd
  4. 为以下9个元素(position() mod $vertical = 1)添加表格。这适用于当前cd元素本身以及包含此表达式的以下8个元素:self::cd | following::cd[position() &lt; $vertical]
  5. 这是XSLT:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:variable name="vertical" select="9" />    <!-- count per table -->
        <xsl:variable name="horizontal" select="4" />  <!-- horizontal count of tables -->
    
        <xsl:template match="/">
            <html>
                <body>
                    <table border="1">
                        <tr bgcolor="#D7BDE2">
                            <th>Blok1</th>
                            <th>Blok2</th>
                            <th>Blok3</th>
                            <th>Blok4</th>
                        </tr>
                      <xsl:apply-templates select="catalog/cd[position() mod ($vertical*$horizontal) = 1]" mode="hor"/>
                    </table>
                </body>
            </html>
        </xsl:template>
    
        <xsl:template match="cd" mode="hor">
            <tr>
                <xsl:apply-templates select="self::cd | following::cd[position() &lt; ($vertical*$horizontal)]" />
            </tr>    
        </xsl:template>
    
        <xsl:template match="cd[position() mod $vertical = 1]">
          <td>
            <table border="1">
                <tr bgcolor="#9acd32">
                    <th>Title1</th>
                    <th>Artist</th>
                </tr>
                <xsl:for-each select="self::cd | following::cd[position() &lt; $vertical]">
                    <tr>
                        <td>
                            <xsl:value-of select="title"/>
                        </td>
                        <td>
                            <xsl:value-of select="artist"/>
                        </td>
                    </tr>
                </xsl:for-each>
            </table>
            </td>
        </xsl:template>
    
        <xsl:template match="text()" />
    
    </xsl:stylesheet>
    

    输出应该是一个令人愉快的4x9矩阵。