xsl:将列表转换为2-D表

Jas*_*n S 6 xml xslt

假设我有这个XML节点:

<items>
    <item>...<item>
    <item>...<item>
    <item>...<item>
    <item>...<item>
    <item>...<item>
    ...
</items>
Run Code Online (Sandbox Code Playgroud)

哪里有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 />
Run Code Online (Sandbox Code Playgroud)

我不知道如何实现的部分是

  • 如何制作谓词来测试item当前节点中的元素数量
  • 如何获取item当前节点中的第N个元素

从评论更新

如何用空<td />元素填充最后一行, 以便每行包含所需的单元格?

Dim*_*hev 5

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>
Run Code Online (Sandbox Code Playgroud)

当此转换应用于以下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>
Run Code Online (Sandbox Code Playgroud)

产生了想要的正确结果:

<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>
Run Code Online (Sandbox Code Playgroud)

说明:

  1. 每行所需的单元格数在external/global参数中指定 $pNumCols.

  2. 模板仅应用于top元素的子元素,其位置是新行的开头 - 它们由表达式生成$k * $pNumCols +1,其中$ k可以是任何整数.

  3. 在处理每一行启动项创建行(模板tr元素)和中它在一个特殊的模式适用模板"copy"$pNumCols使用本身出发.

  4. 匹配itemin模式的模板"copy"只是创建一个单元格(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>
Run Code Online (Sandbox Code Playgroud)

应用于与以前相同的XML文档,此转换产生相同的正确结果.

说明:

  1. <xsl:for-each-group>指令用于选择不同的item元素组,其中每个组包含必须在一行中表示的元素.

  2. 标准XPath 2.0运算符idiv用于此目的.

  3. XSLT 2.0函数current-group()包含必须在当前行中显示的所有项.


Fla*_*ack 4

这是我的工作解决方案

由于您没有提供所需的输出,因此这一特定输出可能无法满足您的需求。

<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>
Run Code Online (Sandbox Code Playgroud)

测试输入:

<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>
Run Code Online (Sandbox Code Playgroud)

输出:

<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>
Run Code Online (Sandbox Code Playgroud)

请注意:您可以动态传递列数。

附加要求和编辑。

<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>
Run Code Online (Sandbox Code Playgroud)

它可以应用于前面的示例或这个简洁的 XML:

<items>
    <item>1</item>
</items>
Run Code Online (Sandbox Code Playgroud)

结果将是:

<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>
Run Code Online (Sandbox Code Playgroud)

请注意:

  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>
Run Code Online (Sandbox Code Playgroud)