2011-01-30 4 views
5

в 2-D Скажем, у меня есть этот XML-узел:XSL: преобразование списка в таблицу

<items> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    <item>...<item> 
    ... 
</items> 

где есть N item узлов.

Теперь я хотел бы преобразовать его в таблицу HTML с 4 столбцами. (например, если 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 элементов в текущем узле
  • как получить элемент Nth item в текущем узле

Обновление от комментариев

Как подушечка последней строки с пустыми <td /> элементов, так что каждая строка содержит точно хотели клетку?

+1

Хороший вопрос, +1. См. Мой ответ на возможное кратчайшее решение, в котором даже не используется явная рекурсия. :) –

+0

Также добавлено решение XSLT 2.0. :) –

ответ

3

Это мое рабочее решение.

Поскольку вы не предоставили желаемый результат, этот конкретный может быть неполным для ваших нужд.

<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) &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> 

Он может быть применен к предыдущему образцу или к этому лаконичный 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> 

ли примечание:

  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> 
+0

Как вы могли отредактировать его так, чтобы он заполнил последнюю строку пустыми элементами, чтобы каждая строка содержала 4 ячейки? –

+0

@ Джейсон С., один момент. – Flack

+0

@Jason S, проверьте мои изменения. – Flack

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> 

Объяснение:

  1. желаемое число клеток в строке задается во внешнем/глобального параметра$pNumCols.

  2. Шаблоны применяются только на таких детей верхнего элемента, положение которого является началом новой строки - они генерируются выражением $k * $pNumCols +1, где $ K может быть любым целым числом.

  3. Шаблон, обработки каждой строки-начиная элемент создает строку (tr элемент), и в нем применяется шаблоны в специальном режиме "copy" для $pNumCols, начиная с самой собой.

  4. шаблона согласования в режиме "copy"item просто создают ячейку (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> 

применяется на том же самом документе XML, как и прежде, это преобразование приводит к тому же, правильный результат.

Объяснение:

  1. <xsl:for-each-group> инструкция используется для выбора различных групп item элементов, где каждая группа содержит элементы, которые должны быть представлены в одном ряду.

  2. Для этого используется стандартный оператор XPath 2.0 idiv.

  3. Функция XSLT 2.0 current-group() содержит все элементы, которые должны быть представлены в текущей строке.

+0

+1. Очень элегантно. – Flack

+0

+1 Хорошие решения. –

+1

Сохранял мой день. Спасибо друг. – Rolf

0

С для-каждой группы вы можете получить более элегантное решение:

<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> 
+0

создает таблицу с четырьмя строками, а не таблицу с 4 столбцами! –

+0

Спасибо.Заменен mod b потоком (a div b) – AtnNn

+0

В XPath/XSLT 2.0 есть оператор idiv (целочисленное деление) –

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> 

С @ вход ответа Флька, выход:

<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> 
+0

+1. Хороший тоже. – Flack

Смежные вопросы