假设我有这个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
元素item
元素从评论中更新
如何用空填充最后一行
<td />
元素使每行 包含完全想要的细胞?
答案 0 :(得分: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>
<强>解释强>:
每行所需的单元格数量在外部/全局参数中指定 $pNumCols
。
模板仅应用于top元素的子元素,其位置是新行的开头 - 它们由表达式$k * $pNumCols +1
生成,其中$ k可以是任何整数。
处理每个行起始项的模板会创建一行(tr
元素),并在其中为{的特殊模式"copy"
应用模板{1}}从自身开始。
模板$pNumCols
中与item
匹配的模板只创建一个单元格("copy"
元素),并在其中输出字符串值td
元素匹配。
<强> II。 XSLT 2.0解决方案:
item
应用于与以前相同的XML文档,此转换产生相同的正确结果。
<强>解释强>:
使用<xsl:for-each-group>
指令来选择<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>
元素的不同组,其中每个组包含必须在一行中表示的元素。
标准XPath 2.0运算符idiv
用于此目的。
XSLT 2.0函数current-group()
包含必须在当前行中显示的所有项。
答案 1 :(得分: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>
测试输入:
<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) < $columns-number">
<xsl:copy-of select="document('')/*/my:layout/td[
position() <= $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
个元素少于列数时,用于添加元素的硬编码数据。如果元素数量少于列数,则只需应用具有相同谓词和不同item
的{{1}}元素。
上次修改。使用计数循环。
mode
答案 2 :(得分: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>
使用@ 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>
答案 3 :(得分:0)
使用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>