xsl:将列表转换为二维表

假设我有这个XML节点:
<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 />
元素使每一行   包含完全想要的细胞?     
已邀请:
那是我的工作解决方案。 由于您未提供所需的输出,因此根据您的需要,此特定输出可能不完整。
<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>
请注意: 当
item
元素少于列数时,硬编码数据添加元素。 额外的硬编码元素,如果列数将改变。 如果元素数量不会少于列数,则可以使用相同的谓词和不同的
mode
应用于
item
元素。 最后编辑。用循环计数。
<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>
    
I. 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>
说明: 每行所需的单元数在外部/全局参数
$pNumCols
中指定。 模板仅应用于top元素的子元素,其位置是新行的开头 - 它们由表达式
$k * $pNumCols +1
生成,其中$ k可以是任何整数。 处理每个行起始项的模板创建一行(
tr
元素),并在其中以特殊模式
"copy"
$pNumCols
应用模板。 在模式
"copy"
中匹配
item
的模板只创建一个单元格(
td
元素),并在其内部输出匹配的
item
元素的字符串值。 II。 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文档,此转换产生相同的正确结果。 说明:
<xsl:for-each-group>
指令用于选择不同的
item
元素组,其中每个组包含必须在一行中表示的元素。 标准XPath 2.0运算符
idiv
用于此目的。 XSLT 2.0函数
current-group()
包含必须在当前行中显示的所有项。     
只是为了样式,这个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>
使用@ Flack的答案输入,输出:
<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>
    
使用for-each-group,您可以获得更优雅的解决方案:
<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>
    

要回复问题请先登录注册