2011-01-30 56 views
5

比方說,我有這樣的XML節點:的xsl:將一個列表轉換成2-d表

<items> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    ... 
</items> 

存在N個item節點。

現在我想將它轉換成一個4列的HTML表格。 (例如,如果N = 12,則有3個完整的行,並且如果N = 27,則有7行,最後有3個單元)

我該如何去做這件事?

我的直覺調用的是做這種方式,其中{{something}}是什麼,我不知道如何實現:

<xsl:template match="items"> 
    <table> 
     <xsl:call-template name="partition-items"> 
     <xsl:with-param name="skip" select="0" /> 
     </xsl:call-template> 
    </table> 
</xsl:template> 

<xsl:template name="partition-items"> 
    <xsl:param name="skip" /> 
    {{ if # of items in current node > $skip, 
      output a row, 
      and call partition-items($skip+4) 
    }} 
<xsl:template /> 

我不知道如何實現這些作品,是

  • 如何使謂詞用於測試item元素#在當前節點
  • 如何獲得第N item元素在當前節點

從評論

更新如何墊的最後一行空 <td />元素使每一行 恰好包含了想要的細胞?

+1

好問題,+1。查看我的答案,可能是最短的解決方案,甚至不使用任何顯式遞歸。 :) – 2011-01-31 01:20:02

+0

也添加了XSLT 2.0解決方案。 :) – 2011-01-31 02:39:22

回答

3

這就是我的工作解決方案

由於您沒有提供所需的輸出,因此您的需求可能不完整。

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html" indent="yes"/> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

</xsl:stylesheet> 

測試輸入:

<items> 
    <item>1</item> 
    <item>2</item> 
    <item>3</item> 
    <item>4</item> 
    <item>5</item> 
    <item>6</item> 
    <item>7</item> 
    <item>8</item> 
    <item>9</item> 
    <item>10</item> 
    <item>11</item> 
    <item>12</item> 
    <item>13</item> 
    <item>14</item> 
    <item>15</item> 
    <item>16</item> 
    <item>17</item> 
    <item>18</item> 
    <item>19</item> 
    <item>20</item> 
    <item>21</item> 
    <item>22</item> 
    <item>23</item> 
    <item>24</item> 
    <item>25</item> 
    <item>26</item> 
    <item>27</item> 
</items> 

輸出:

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
    </tr> 
</table> 

待辦事項:可以動態傳遞的列數。

附加要求和編輯。

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:my="http://localhost" 
    exclude-result-prefixes="my"> 
    <xsl:output method="html" indent="yes"/> 

    <my:layout> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
     <td/><td/><td/><td/> 
    </my:layout> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
      <xsl:if test="count($nodelist) &lt; $columns-number"> 
       <xsl:copy-of select="document('')/*/my:layout/td[ 
        position() &lt;= $columns-number - count($nodelist) 
        ]"/> 
      </xsl:if> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

</xsl:stylesheet> 

它可以應用到以前的樣品或在本簡明的XML:

<items> 
    <item>1</item> 
</items> 

結果將是:

<table> 
    <tr> 
     <td>1</td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
     <td xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://localhost"></td> 
    </tr> 
</table> 

待辦事項:

  1. 添加元素的硬編碼數據,當列數少於item時。
  2. 額外的硬編碼元素,如果列數將會改變。

如果將永遠不會成爲比列數少的元素,你可以只適用於item元素與同謂語和不同mode

最後編輯。用計數的循環。

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html" indent="yes"/> 

    <xsl:template match="/*"> 
     <table> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="item"/> 
      </xsl:call-template> 
     </table> 
    </xsl:template> 

    <xsl:template name="make-columns"> 
     <xsl:param name="nodelist"/> 
     <xsl:param name="columns-number" select="4"/> 

     <tr> 
      <xsl:apply-templates select="$nodelist[ 
          not(position() > $columns-number) 
          ]"/> 
      <xsl:if test="count($nodelist) &lt; $columns-number"> 
       <xsl:call-template name="empty-cells"> 
        <xsl:with-param name="finish" select="$columns-number - count($nodelist)"/> 
       </xsl:call-template> 
      </xsl:if> 
     </tr> 

     <!-- If some nodes are left, recursively call current 
     template, passing only nodes that are left --> 
     <xsl:if test="count($nodelist) > $columns-number"> 
      <xsl:call-template name="make-columns"> 
       <xsl:with-param name="nodelist" select="$nodelist[ 
             position() > $columns-number 
             ]"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="item"> 
     <td> 
      <xsl:apply-templates/> 
     </td> 
    </xsl:template> 

    <xsl:template name="empty-cells"> 
     <xsl:param name="finish"/> 
     <td/> 
     <xsl:if test="not($finish = 1)"> 
      <xsl:call-template name="empty-cells"> 
       <xsl:with-param name="finish" select="$finish - 1"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

如何編輯它,以便填充空元素的最後一行,以便每行包含4個單元格? – 2011-01-30 23:23:51

+0

@Jason S,一會兒。 – Flack 2011-01-30 23:26:37

+0

@Jason S,檢查我的編輯。 – Flack 2011-01-30 23:46:12

5

一XSLT 1.0解決方案:

這裏可能是特別不要求明確的遞歸最短的解決方案之一:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:param name="pNumCols" select="4"/> 

<xsl:template match="/*"> 
    <table> 
    <xsl:apply-templates select="*[position() mod $pNumCols =1]"/> 
    </table> 
</xsl:template> 

<xsl:template match="item"> 
    <tr> 
    <xsl:apply-templates mode="copy" select= 
    ". | following-sibling::*[not(position() >= $pNumCols)]"/> 
    </tr> 
</xsl:template> 

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

當施加這種轉變關於以下XML文件

<items> 
    <item>1</item> 
    <item>2</item> 
    <item>3</item> 
    <item>4</item> 
    <item>5</item> 
    <item>6</item> 
    <item>7</item> 
    <item>8</item> 
    <item>9</item> 
    <item>10</item> 
    <item>11</item> 
    <item>12</item> 
    <item>13</item> 
    <item>14</item> 
    <item>15</item> 
    <item>16</item> 
    <item>17</item> 
    <item>18</item> 
    <item>19</item> 
    <item>20</item> 
    <item>21</item> 
    <item>22</item> 
    <item>23</item> 
    <item>24</item> 
    <item>25</item> 
    <item>26</item> 
    <item>27</item> 
</items> 

的想要的,正確的結果產生

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
    </tr> 
</table> 

說明

  1. 每行細胞的想要的數量在外部/全局參數$pNumCols被指定。它們由表達式$k * $pNumCols +1,其中$ k可以爲任何整數產生 -

  2. 模板只在頂部元件,其位置是一個新行的開始的這樣的兒童施用。

  3. 在處理每一行啓動項創建行(tr元素)的模板,並將其應用模板,在特殊模式下"copy"$pNumCols開始本身。

  4. 匹配模式"copy"一個item簡單地創建在其內部的細胞(td元件)和輸出的item元件的字符串值的模板相匹配。

二, XSLT 2.0溶液

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:param name="pNumCols" select="4"/> 

    <xsl:template match="items"> 
     <table> 
      <xsl:for-each-group select="item" 
      group-by="(position()-1) idiv $pNumCols"> 
       <tr> 
        <xsl:for-each select="current-group()"> 
         <td> 
          <xsl:apply-templates/> 
         </td> 
        </xsl:for-each> 
       </tr> 
      </xsl:for-each-group> 
     </table> 
    </xsl:template> 
</xsl:stylesheet> 

如之前對相同的XML文檔應用,該變換產生相同的,正確的結果。

說明

  1. <xsl:for-each-group>指令用於選擇item元件的不同組,其中每個組包含必須在一個行來表示的元素。

  2. 爲此目的使用標準的XPath 2.0運營商idiv

  3. XSLT 2.0函數current-group()包含必須出現在當前行中的所有項目。

0

隨着-各個羣組,你可以得到一個更優雅的解決方案:

<xsl:template match="items"> 
    <table> 
    <xsl:for-each-group select="item" group-by="ceiling(position() div $column_width)"> 
     <tr> 
     <xsl:for-each select="current-group()"> 
      <td> 
      <xsl:apply-templates/> 
      </td> 
     </xsl:for-each> 
     </tr> 
    </xsl:for-each-group> 
    </table> 
</xsl:template> 
1

只是爲了風格,XSLT 1.0樣式:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:param name="pColumns" select="4"/> 
    <xsl:template match="/*"> 
     <table> 
      <xsl:apply-templates select="*[position() mod $pColumns = 1]"/> 
     </table> 
    </xsl:template> 
    <xsl:template match="item"> 
     <xsl:variable name="vItems" 
         select=".|following-sibling::*[$pColumns > position()]"/> 
     <tr> 
      <xsl:apply-templates select="$vItems" mode="makeCell"/> 
      <xsl:call-template name="fillRow"> 
       <xsl:with-param name="pItems" 
           select="$pColumns - count($vItems)"/> 
      </xsl:call-template> 
     </tr> 
    </xsl:template> 
    <xsl:template match="item" mode="makeCell"> 
     <td> 
      <xsl:value-of select="."/> 
     </td> 
    </xsl:template> 
    <xsl:template name="fillRow"> 
     <xsl:param name="pItems" select="0"/> 
     <xsl:if test="$pItems"> 
      <td/> 
      <xsl:call-template name="fillRow"> 
       <xsl:with-param name="pItems" select="$pItems - 1"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

隨着@弗萊克的回答輸入,輸出:

<table> 
    <tr> 
     <td>1</td> 
     <td>2</td> 
     <td>3</td> 
     <td>4</td> 
    </tr> 
    <tr> 
     <td>5</td> 
     <td>6</td> 
     <td>7</td> 
     <td>8</td> 
    </tr> 
    <tr> 
     <td>9</td> 
     <td>10</td> 
     <td>11</td> 
     <td>12</td> 
    </tr> 
    <tr> 
     <td>13</td> 
     <td>14</td> 
     <td>15</td> 
     <td>16</td> 
    </tr> 
    <tr> 
     <td>17</td> 
     <td>18</td> 
     <td>19</td> 
     <td>20</td> 
    </tr> 
    <tr> 
     <td>21</td> 
     <td>22</td> 
     <td>23</td> 
     <td>24</td> 
    </tr> 
    <tr> 
     <td>25</td> 
     <td>26</td> 
     <td>27</td> 
     <td /> 
    </tr> 
</table>