Хорошо
Я объединил идею @Dimitre Novatchev из ответов этого поста и @ Tomalak из [XSLT]: рендеринг последовательности узлов в виде таблицы M x N post. Мне очень понравилось решение @ Tomalak с переменной $perRow
и шаблон <xsl:template name="filler">
для работы с пустыми ячейками.
С идеей, взятой из ответа @Dimitre Novatchev, я держусь за $trStartPos
. Затем я вычисляю $lowerBoundry
и $upperBoundry
, которые используются для накопления всех элементов, которые должны появиться в строке. Может быть, есть более элегантный способ сделать этот расчет - пожалуйста, дайте мне знать, если вы придумали один, я был бы очень признателен!
XSLT-преобразование
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Select only required elements -->
<xsl:variable name="tableData"
select="/nums/A | /nums/B | /nums/C | /nums/D | /nums/E "/>
<xsl:variable name="perRow" select="2"/>
<xsl:template match="/">
<table border="1">
<tbody>
<xsl:apply-templates
select="$tableData[position() mod $perRow = 1]" mode="tr"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="nums/*" mode="tr">
<xsl:variable name="trStartPos" select="position()" />
<xsl:variable name="upperBoundry" select="$trStartPos * $perRow" />
<xsl:variable name="lowerBoundry" select="$upperBoundry - $perRow" />
<tr>
<xsl:variable name="tdsData"
select="$tableData[(position() > $lowerBoundry) and (position() <= $upperBoundry)]" />
<xsl:apply-templates select="$tdsData" mode="td"/>
<!-- fill up the last row - @Tomalak's solution -->
<xsl:if test="count($tdsData) < $perRow">
<xsl:call-template name="filler">
<xsl:with-param name="rest" select="$perRow - count($tdsData)" />
</xsl:call-template>
</xsl:if>
</tr>
</xsl:template>
<!-- Templates for specific elements could be easily added with appropriate info to
be displayed depending on the element. This one is general just to display
elements' name and value -->
<xsl:template match="nums/*" mode="td">
<td>
El. name: <xsl:value-of select="local-name()"/> -
El. value: <xsl:value-of select="."/>
</td>
</xsl:template>
<!-- @Tomalak solution (please read beginning of this answer for reference) -->
<xsl:template name="filler">
<xsl:param name="rest" select="0" />
<xsl:if test="$rest">
<td> </td>
<xsl:call-template name="filler">
<xsl:with-param name="rest" select="$rest - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
применяется к следующему XML
<nums>
<A>A-01</A>
<num>02</num>
<num>03</num>
<num>04</num>
<B>B-05</B>
<num>06</num>
<num>07</num>
<C>C-08</C>
<num>09</num>
<D>D-10</D>
<num>11</num>
<num>12</num>
<num>13</num>
<E>E-14</E>
<num>15</num>
</nums>
приводит к следующему выводу
<table border="1">
<tbody>
<tr>
<td>
El. name: A -
El. value: A-01
</td>
<td>
El. name: B -
El. value: B-05
</td>
</tr>
<tr>
<td>
El. name: C -
El. value: C-08
</td>
<td>
El. name: D -
El. value: D-10
</td>
</tr>
<tr>
<td>
El. name: E -
El. value: E-14
</td>
<td> </td>
</tr>
</tbody>
</table>