Этот вопрос очень похож на XSL: преобразование xml в отсортированную многоколоночную HTML-таблицу
Но (к сожалению) есть дополнительное требование: это должен быть XSLT 1.0 без функций расширения, т.е.без использования функции набора узлов.
Это мой упрощенный XML:
<demo>
<config n_columns="3" />
<messages>
<msg date="2011-07-06" title="2nd message" />
<title>message list</title>
<msg date="2011-07-05" title="4th message" />
<msg date="2011-07-06" title="3rd message" />
<msg date="2011-07-07" title="1st message" />
</messages>
</demo>
Использование этой таблицы стилей:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/">
<xsl:apply-templates select="demo/messages">
<xsl:with-param name="n_columns" select="number(/demo/config/@n_columns)" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="messages">
<xsl:param name="n_columns" />
<div>
<xsl:value-of select="concat(./title, ' (', $n_columns, ' columns)')" />
</div>
<table>
<xsl:variable name="cells" select="msg" />
<xsl:apply-templates select="$cells[(position() - 1) mod $n_columns = 0]"
mode="row">
<xsl:with-param name="n_columns" select="$n_columns" />
<xsl:with-param name="cells" select="$cells" />
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="msg" mode="row">
<xsl:param name="n_columns" />
<xsl:param name="cells" />
<xsl:variable name="n_row" select="position()" />
<xsl:variable name="row_cells"
select="$cells[position() > ($n_row - 1) * $n_columns][position() <= $n_columns]" />
<tr>
<xsl:apply-templates select="$row_cells" mode="cell" />
<xsl:call-template name="empty-cells">
<xsl:with-param name="n" select="$n_columns - count($row_cells)" />
</xsl:call-template>
</tr>
</xsl:template>
<xsl:template match="msg" mode="cell">
<td>
<xsl:value-of select="@title" />
</td>
</xsl:template>
<xsl:template name="empty-cells">
<xsl:param name="n" />
<xsl:if test="$n > 0">
<td>
<xsl:attribute name="colspan">
<xsl:value-of select="$n" />
</xsl:attribute>
<xsl:text> </xsl:text>
</td>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Создание этого фрагмента HTML в качестве вывода:
<div>message list (3 columns)</div>
<table>
<tr>
<td>2nd message</td>
<td>4th message</td>
<td>3rd message</td>
</tr>
<tr>
<td>1st message</td>
<td colspan="2"> </td>
</tr>
</table>
Чего явно не хватает, так это части сортировки ...
Переопределение переменной "ячейки" следующим образом - это на самом деле то, что мне нужно:
<xsl:variable name="cells">
<xsl:for-each select="msg">
<xsl:sort select="@date" order="descending" />
<xsl:sort select="@title" />
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
Но сейчасЯ должен определить другую переменную, чтобы преобразовать RTF в список узлов и передать ее в шаблон, который я применяю.
<xsl:variable name="sCells" select="ext:node-set($cells)/*" />
В результате получится следующий фрагмент HTML:
<div>message list (3 columns)</div>
<table>
<tr>
<td>1st message</td>
<td>2nd message</td>
<td>3rd message</td>
</tr>
<tr>
<td>4th message</td>
<td colspan="2"> </td>
</tr>
</table>
К сожалению, мой XSLT-движок (SAP XML toolkit для java) не поддерживает эту (или похожую) функцию расширения.Поэтому я ищу другое решение, которое не требует функции расширения набора узлов.
Я потратил довольно много времени на чтение всевозможных форумов и т. Д., Но я действительно не могу понять это.Возможно, у кого-то есть хорошая идея для альтернативного подхода?tnx!
Это продолжение, основанное на (немного расширенном) решении Димитра.Этот XML-вход
<demo>
<config n_columns="3" />
<messages>
<msg date="2011-07-06" title="2nd message" />
<title>message list</title>
<msg date="2011-07-05" title="4th message" />
<msg date="2011-07-06" title="3rd message" />
<msg date="2011-07-07" title="1st message" />
<msg date="2011-07-05" title="5th message" />
<msg date="2011-07-05" title="7th message" />
<msg date="2011-07-05" title="6th message" />
</messages>
</demo>
в сочетании с этой таблицей стилей XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:variable name="vNumCols" select="/*/config/@n_columns" />
<xsl:variable name="vCells" select="/*/messages/msg" />
<xsl:variable name="vNumCells" select="count($vCells)" />
<xsl:variable name="vNumRows" select="ceiling($vNumCells div $vNumCols)" />
<xsl:variable name="vIndexPatternLength"
select="string-length(concat('', $vNumCells))" />
<xsl:variable name="vIndexPattern">
<xsl:call-template name="padding">
<xsl:with-param name="length" select="$vIndexPatternLength" />
<xsl:with-param name="chars" select="'0'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vSortedIndex">
<xsl:for-each select="$vCells">
<xsl:sort select="@date" order="descending" />
<xsl:sort select="@title" />
<xsl:value-of
select="format-number(count(preceding-sibling::msg) + 1,
$vIndexPattern)" />
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="demo/messages" />
</xsl:template>
<xsl:template match="messages">
<table>
<xsl:for-each select="$vCells[not(position() > $vNumRows)]">
<xsl:variable name="vRow" select="position()" />
<tr>
<xsl:for-each select="$vCells[not(position() > $vNumCols)]">
<xsl:variable name="vCol" select="position()" />
<xsl:variable name="vCell"
select="($vRow - 1) * $vNumCols + $vCol" />
<xsl:variable name="vIndex"
select="substring($vSortedIndex,
($vCell - 1) * $vIndexPatternLength + 1,
$vIndexPatternLength)" />
<xsl:variable name="vMessage"
select="$vCells[position() = $vIndex]" />
<xsl:choose>
<xsl:when test="$vMessage">
<xsl:apply-templates select="$vMessage"
mode="cell" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="empty-cell" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="msg" mode="cell">
<td>
<xsl:apply-templates select="." />
</td>
</xsl:template>
<xsl:template match="msg">
<xsl:value-of select="concat(@date, ' : ', @title)" />
</xsl:template>
<xsl:template name="empty-cell">
<td>
<xsl:text> </xsl:text>
</td>
</xsl:template>
<!-- http://www.exslt.org/str/functions/padding/ -->
<xsl:template name="padding">
<xsl:param name="length" select="0" />
<xsl:param name="chars" select="' '" />
<xsl:choose>
<xsl:when test="not($length) or not($chars)" />
<xsl:otherwise>
<xsl:variable name="string"
select="concat($chars, $chars, $chars, $chars, $chars,
$chars, $chars, $chars, $chars, $chars)" />
<xsl:choose>
<xsl:when test="string-length($string) >= $length">
<xsl:value-of select="substring($string, 1, $length)" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="padding">
<xsl:with-param name="length" select="$length" />
<xsl:with-param name="chars" select="$string" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
создает этот HTML-вывод
<table>
<tr>
<td>2011-07-07 : 1st message</td>
<td>2011-07-06 : 2nd message</td>
<td>2011-07-06 : 3rd message</td>
</tr>
<tr>
<td>2011-07-05 : 4th message</td>
<td>2011-07-05 : 5th message</td>
<td>2011-07-05 : 6th message</td>
</tr>
<tr>
<td>2011-07-05 : 7th message</td>
<td> </td>
<td> </td>
</tr>
</table>
Спасибо, Димитр!