Как использовать xsl:for-each
в xslt, чтобы получить значение многократно из XML-файла и отобразить его в формате таблицы
Например: XML-файл похож
<order>
<item name ="a"/>
<item name ="b"/>
<item name ="c"/>
<item name ="d"/>
<item name ="e"/>
<item name ="f"/>
<item name ="g"/>
</order>
и вывод должен быть
a b c d
e f g
цикл должен считать объект и если это - divisble 4 это
должен закрыть текущую строку и добавить новую строку и так далее..
я использую следующий xslt для этого
но я не могу отобразить его в формате таблицы
<xsl:template match="/">
<html>
<body>
<xsl:call-template name ="incr">
<xsl:with-param name ="value">1</xsl:with-param>
<xsl:with-param name ="limit">
<xsl:value-of select ="count(//item)"/>
</xsl:with-param>
</xsl:call-template>
</body>
</html>
</xsl:template >
<xsl:template name="incr">
<xsl:param name="value"/>
<xsl:param name ="limit"/>
<xsl:if test ="$value!=$limit+1">
<xsl:value-of select ="//item[$value]/@name"/>
<xsl:if test ="$value mod 4 =0">
<br/>
<br/>
</xsl:if>
<xsl:call-template name ="incr">
<xsl:with-param name ="value" select ="$value+1"/>
<xsl:with-param name ="limit" select ="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
помогите мне сделать это
Заранее спасибо
<table>
<tr>
<xsl:for-each select="//order/item">
<td>
<xsl:value-of select ="current()/@name"/>
</td>
<xsl:if test="position() mod 4 = 0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr><tr>]]></xsl:text>
</xsl:if>
</xsl:for-each>
</tr>
</table>
Это преобразование:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vNumCols" select="4"/>
<xsl:template match="/*">
<table>
<xsl:for-each select=
"item[position() mod $vNumCols = 1]">
<tr>
<xsl:for-each select=
". | following-sibling::*
[not(position() >= $vNumCols)]">
<td><xsl:value-of select="@name"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному XML-документу дает желаемые правильные результаты:
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
<td>d</td>
</tr>
<tr>
<td>e</td>
<td>f</td>
<td>g</td>
</tr>
</table>
Я не уверен на 100%, но приведенный ниже код должен это сделать:
<table>
<tr>
<xsl:for-each select="//order/item">
<td>
<xsl:value-of select ="current()/@name"/>
</td>
<xsl:if test="position() mod 4 = 0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr><tr>]]></xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:variable name="item_count_mod4" select="count(//order/item) mod 4"/>
<xsl:choose>
<xsl:when test="$item_count_mod4 = 1">
<td></td><td></td><td></td>
</xsl:when>
<xsl:when test="$item_count_mod4 = 2">
<td></td><td></td>
</xsl:when>
<xsl:when test="$item_count_mod4 = 3">
<td></td>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</tr>
</table>
О проблемах любой сложности в XSLT нужно думать не так: "Как мне написать программу для получения Y, учитывая X в качестве входных данных?", а скорее так: "Учитывая выход Y, какой X я должен преобразовать, чтобы получить его?". Этот принцип не так-то просто понять (или сформулировать), но как только вы его поймете, кажущиеся сложными проблемы в XSLT станут тривиальными.
Если выход представляет собой серию tr
элементов, как, например, здесь:
<tr>
<td>a</td><td>b</td><td>c</td>
</tr>
<tr>
<td>d</td><td>e</td><td>f</td>
</tr>
<tr>
<td>g</td><td>h</td><td>i</td>
</tr>
<tr>
<td>j</td><td/><td/>
</tr>
то выходных элементов, по сути, четыре. Значит, должно быть четыре входных элемента.
Первый вопрос - какие четыре? Совершенно ясно, что это будут 1-й, 4-й, 7-й и 10-й - то есть, каждые 3 элемента, начиная с первого. Итак, ваша отправная точка - преобразование этих четырех элементов:
<xsl:apply-templates select="/order/item[position() mod 3 = 1]"/>
Хорошо, и теперь, когда мы выбрали каждый третий элемент, как нам создать tr
из него и элементов сразу после него? Используя ось following-sibling
:
<xsl:template match="item">
<tr>
<td><xsl:value-of select="@name"/></td>
<td><xsl:value-of select="following-sibling::item[1]/@name"/></td>
<td><xsl:value-of select="following-sibling::item[2]/@name"/></td>
</tr>
</xsl:template>
Это хорошо, насколько это возможно. Но здесь довольно много дублирующегося кода, и многое придется изменить, если вы захотите (скажем) изменить количество столбцов с 3 на 6. Вы можете устранить дублирование кода, сделав другой шаблон:
<xsl:template match="item">
<tr>
<xsl:apply-templates select="@name | following-sibling::item[position() <= 3]/@name"/>
</tr>
</xsl:template>
<xsl:template match="@name">
<td><xsl:value-of select="."/></td>
</xsl:template>
И вы можете параметризовать количество колонок, поместив его в переменную, как это сделал Дмитрий в своем примере.