2016-03-19 1 views
1

Для определенного формата вывода (не HTML-подобный), мне нужно, чтобы преобразовать HTML таблицу в «квадрате» таблицу, где каждый colspan и rowspan не только указанная в родительской ячейки, но также следует правильное количество пустых ячеек.Преобразовать

Например, простой HTML таблицы

<table> 
    <tr> 
     <th>test</th> 
     <th colspan="2">span 1/2</th> 
     <th colspan="3">span 2/2</th> 
    </tr> 
    <tr> 
     <td>col 1</td> 
     <td>col 2</td> 
     <td>col 3</td> 
     <td>col 4</td> 
     <td>col 5</td> 
     <td>col 6</td> 
    </tr> 
</table> 

должны быть переведены на

<table> 
    <tr> 
     <th>test</th> 
     <th colspan="2">span 1/2</th> 
     <th /> <!-- < empty cell added --> 
     <th colspan="3">span 2/2</th> 
     <th /> <!-- < empty cell added --> 
    </tr> 
    .. 

(примечание: формат вывода использует совершенно иной синтаксис, это только для ясности!)

и, соответственно, ряды должны распространяться до следующих <tr> линий:

<table><tr><td rowspan="3" /><td rowspan="2" /><td /></tr> 
    <tr><td>data</td></tr> 
    <tr><td>data</td><td>data</td></tr> 
</table> 

, который должен выйти в

<table> 
    <tr><td /><td /><td /></tr> 
    <tr><td /><td /><td>data</td></tr> <!-- 2 empty cells added --> 
    <tr><td /><td>data</td><td>data</td></tr> <!-- 1 empty cell added --> 
<table> 

Обработка colspan проста:

<xsl:template name="add-empty"> 
    <xsl:param name="repeat" /> 

    <xsl:if test="$repeat &gt; 1"> 
     <td class="empty" /> 
     <xsl:call-template name="add-empty"> 
      <xsl:with-param name="repeat" select="$repeat - 1" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="th|td"> 
    <td> 
     <xsl:apply-templates /> 
    </td> 
    <xsl:if test="@colspan"> 
     <xsl:call-template name="add-empty"> 
      <xsl:with-param name="repeat" select="@colspan" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

Это добавит одного th или td, проверить каждого из них colspan и вставьте столько пустых ячеек при необходимости с рекурсивный вызов шаблону add-empty. Атрибут класса empty предназначен только для отладки.

Проблема в rowspan с. Для правильной работы необходимо сканировать более каждыеtr и сохранить количество столбцов, которые должны быть пустыми. Это итерация будет что-то вроде

<xsl:if test="position() &gt; 1"> 
    <xsl:variable name="currentRow" select="position()" /> 
    <xsl:for-each select="../tr[position() &lt; $currentRow]"> 
     <xsl:message>testing <xsl:value-of select="." /></xsl:message> 
    </xsl:for-each> 
</xsl:if> 

- это не обязательно должны быть вызваны в первом ряду, потому что только colspan сек необходимость добавления. Таким образом, вопрос двояка: как бы я создал список наборов ячеек, чтобы добавить правильный набор для текущей строки ? И с таким списком, как я могу перебирать и этот список (до тех пор, пока общее количество столбцов в таблице) и элементов каждой строки th|td?

Последнее является проблемой, потому что я могу перебрать либо клетка установить, используя нечто вроде

<xsl:for-each select="1 to string-length(cell-set)"> 
    <xsl:if test="substring($cell-set, ., 1) = 'E'> 
    .. empty .. 
    ... 
</xsl:for-each> 

(если cell-set это строка), или над «текущим» tr содержимого с использованием

<xsl:for-each select="th|td"> 
    .. 

В этом случае нет прямого отношения к содержанию cell-set. С первым, я не знаю, какой индекс td|th вставить, со вторым я не знаю, когда вставить пробел.

+1

Как вы, кажется, перешли на XSLT 2.0 вы можете проверить http://andrewjwelch.com/code/xslt/ table/table-normalization.html как идея, он копирует существующую ячейку вместо добавления пустой, но не такой, какой должен сделать алгоритм, если я не ошибаюсь в отношении ваших требований. –

+0

@MartinHonnen: Я не так много «переключился», как немного опоздал, понял, что мне лучше указать, что :) - задача кажется достаточно сложной, даже * с функциями * 2.0. Посмотрите на ссылку и ваш ответ a.s.a.p. (Ah - например, 'for-each select =" 1 to @colspan "вместо моего собственного рекурсивного шаблона в 1.0 ... аккуратно!) – usr2564301

+0

@RadLexus Посмотрите, поможет ли это: http://stackoverflow.com/a/27217608/3016153 –

ответ

3

на основе http://andrewjwelch.com/code/xslt/table/table-normalization.html, который я уже упоминал в ссылке вы можете использовать:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="xs"> 

    <xsl:output indent="yes" omit-xml-declaration="yes" /> 

    <xsl:variable name="table_with_no_colspans"> 
     <xsl:apply-templates mode="colspan" /> 
    </xsl:variable> 

    <xsl:variable name="table_with_no_rowspans"> 
     <xsl:for-each select="$table_with_no_colspans"> 
      <xsl:apply-templates mode="rowspan" /> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:template match="/"> 
     <xsl:apply-templates select="$table_with_no_rowspans" mode="final" /> 
    </xsl:template> 

    <xsl:template match="@*|*" mode="#all"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|*" mode="#current" /> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="td | th" mode="colspan"> 
     <xsl:choose> 
      <xsl:when test="@colspan"> 
       <xsl:copy> 
        <xsl:copy-of select="@* except @colspan"/> 
        <xsl:apply-templates/> 
       </xsl:copy> 
       <xsl:for-each select="2 to @colspan"> 
        <td/> 
       </xsl:for-each> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy-of select="." /> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

    <!-- make sure it works for both table/tr and table/tbody/tr --> 
    <xsl:template match="tbody|table[not(tbody)]" mode="rowspan"> 
     <xsl:copy> 
      <xsl:copy-of select="tr[1]" /> 
      <xsl:apply-templates select="tr[2]" mode="rowspan"> 
       <xsl:with-param name="previousRow" select="tr[1]" /> 
      </xsl:apply-templates> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="tr" mode="rowspan"> 
     <xsl:param name="previousRow" as="element()" /> 

     <xsl:variable name="currentRow" select="." /> 

     <xsl:variable name="normalizedTDs"> 
      <xsl:for-each select="$previousRow/*"> 
       <xsl:choose> 
        <xsl:when test="@rowspan &gt; 1"> 
         <xsl:copy> 
          <xsl:attribute name="rowspan"> 
           <xsl:value-of select="@rowspan - 1" /> 
          </xsl:attribute><!-- 
          <xsl:copy-of select="@*[not(name() = 'rowspan')]" /> 
          <xsl:copy-of select="node()" /> 
         --></xsl:copy> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:copy-of select="$currentRow/*[1 + count(current()/preceding-sibling::*[not(@rowspan) or (@rowspan = 1)])]" /> 
        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:for-each> 
     </xsl:variable> 

     <xsl:variable name="newRow" as="element(tr)"> 
      <xsl:copy> 
       <xsl:copy-of select="$currentRow/@*" /> 
       <xsl:copy-of select="$normalizedTDs" /> 
      </xsl:copy> 
     </xsl:variable> 

     <xsl:copy-of select="$newRow" /> 

     <xsl:apply-templates select="following-sibling::tr[1]" mode="rowspan"> 
      <xsl:with-param name="previousRow" select="$newRow" /> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="td | th" mode="final"> 
     <xsl:choose> 
      <xsl:when test="@rowspan"> 
       <xsl:copy> 
        <xsl:copy-of select="@* except @rowspan" /> 
        <xsl:copy-of select="node()" /> 
       </xsl:copy> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:copy-of select="." /> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Работает с удовольствием. Мне все еще нужно выяснить, как и где генерируются 'tr' (мне нужно перевести их на тегированный текст), но не беспокойтесь, я доберусь туда. Я заметил, что значение 'colspan' тихо отбрасывается, что логично, но мне все еще нужно это значение. Добавление '' после того, как ваш '' исправил это. (Для заинтересованных lurkers: большую часть 'xsl: copy' можно безопасно перевести на' xsl: apply-templates', с дополнительным 'mode', чтобы скопировать только его содержимое.) – usr2564301

+1

Если вы хотите скопировать' colspan', тогда do '' вместо '' достаточно, поскольку последний копирует все атрибуты, кроме 'colspan' атрибут, в то время как первый будет копировать все атрибуты. Что касается элементов 'tr', в режиме' colspan' и в режиме 'final', они просто копируются шаблоном преобразования идентификаторов' 'настроен для всех режимов. В режиме 'rowspan' есть' '. Поэтому вам, вероятно, нужен шаблон 'match =" tr "mode =" final "'. –