Как правильно указать colname для таблиц? - PullRequest
1 голос
/ 13 января 2012

Мой исходный XML выглядит так:

<TABLE>
  <ROW>
    <CELL ROWSPAN="3"> Test </CELL>
    <CELL ROWSPAN="2"> Test </CELL>
    <CELL ROWSPAN="1"> Test </CELL>
    <CELL ROWSPAN="3"> Test </CELL>
    <CELL ROWSPAN="1"> Test </CELL>
  </ROW>
  <ROW>
    <CELL ROWSPAN="1"> Test </CELL>
    <CELL ROWSPAN="1"> Test </CELL>
  </ROW>
</TABLE>

Правильный преобразованный вывод должен выглядеть так:

<tbody>
  <row>
    <entry colname="1"> Test </entry>
    <entry colname="2"> Test </entry>
    <entry colname="3"> Test </entry>
    <entry colname="4"> Test </entry>
    <entry colname="5"> Test </entry>
 </row>
 <row>
   <entry colname="3"> Test </entry>
   <entry colname="5"> Test </entry>
 </row>
</tbody>

Как видите, сложная часть - это элемент второго ряда. Поскольку первая строка имеет несколько ячеек, занимающих несколько строк, это повлияет на имена столбцов второй строки, поэтому первая запись второй строки начинается с имени «3» вместо «1». Я не знаю, как нарисовать здесь таблицу, но если вы нарисуете ее на бумаге, вы легко поймете.

В настоящее время у меня есть следующий xsl, который может частично захватить это (я пропустил другую информацию, так как у меня только проблемы с @colname)

<xsl:template match="CELL">
   <xsl:if test="../preceding-sibling::ROW[1]/CELL[1]/@ROWSPAN > 1">
     <xsl:attribute name="colname" select="position()+count(../preceding-sibling::ROW[1]/CELL[@ROWSPAN>1])"/>
    ..
   </xsl:if>
</xsl:template>

Это не будет хорошо работать, так как будет включать в себя все CELL, которые имеют многократный диапазон строк, поэтому результат будет выглядеть так:

  <row>
    <entry colname="4"> Test </entry>
    <entry colname="5"> Test </entry>
  </row> 

Хотя первая запись действительно должна начинаться с 3.

Я обнаружил, что описать эту проблему немного сложно, но я постараюсь изо всех сил, если потребуется дополнительная информация, пожалуйста, оставьте комментарий ниже.

Ответы [ 2 ]

2 голосов
/ 13 января 2012

Это решение довольно сложное, и у меня есть ноющее чувство, что может быть лучший способ сделать это, но, похоже, оно работает:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="TABLE">
    <tbody>
      <xsl:apply-templates select="ROW[1]" />
    </tbody>
  </xsl:template>

  <xsl:template match="ROW">
    <xsl:param name="occupiedcols" />
    <row>
      <xsl:apply-templates select="CELL[1]">
        <xsl:with-param name="occupiedcols" select="$occupiedcols" />
      </xsl:apply-templates>
    </row>
    <xsl:apply-templates select="following-sibling::ROW[1]">
      <xsl:with-param name="occupiedcols">
        <xsl:apply-templates select="CELL[1]" mode="getoccupied">
          <xsl:with-param name="occupiedcols" select="$occupiedcols" />
        </xsl:apply-templates>
        <xsl:text></xsl:text>
      </xsl:with-param>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="CELL">
    <xsl:param name="occupiedcols" />
    <xsl:param name="col" select="1" />
    <xsl:variable name="thiscol" select="$col + string-length(substring-before(substring($occupiedcols,$col,255),'0'))" />
    <xsl:element name="entry">
      <xsl:attribute name="colname">
        <xsl:value-of select="$thiscol" />
      </xsl:attribute>
    </xsl:element>
    <xsl:apply-templates select="following-sibling::CELL[1]">
      <xsl:with-param name="occupiedcols" select="$occupiedcols"/>
      <xsl:with-param name="col" select="$thiscol + 1" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="CELL" mode="getoccupied">
    <xsl:param name="occupiedcols" />
    <xsl:param name="col" select="1" />
    <xsl:variable name="thiscol" select="$col + string-length(substring-before(substring($occupiedcols,$col,255),'0'))" />
    <xsl:choose>
      <xsl:when test="contains(substring($occupiedcols,$col,255),'0')">
        <xsl:value-of select="translate(substring-before(substring($occupiedcols,$col,255),'0'),'0123456789','-012345678')" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="translate(substring($occupiedcols,$col,255),'123456789','012345678')" />
      </xsl:otherwise>
    </xsl:choose>
    <xsl:value-of select="@ROWSPAN - 1" />
    <xsl:if test="not(following-sibling::CELL)">
      <xsl:value-of select="translate(substring($occupiedcols,$thiscol + 1, 255),'0123456789','0012345678')" />
    </xsl:if>
    <xsl:apply-templates select="following-sibling::CELL[1]" mode="getoccupied">
      <xsl:with-param name="occupiedcols" select="$occupiedcols"/>
      <xsl:with-param name="col" select="$thiscol + 1" />
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

Есть одна известная проблема: она сломается, если ячейка занимает более 9 строк. На самом деле это довольно легко адаптировать, если это проблема.

Кроме того, он не поддерживает использование COLSPAN.

Он работает, передавая строку цифр, детализирующую количество строк, которые все еще имеют ячейку для каждого столбца, поэтому вторая строка получит в вашем примере значение 21020 и вычислит атрибут colname на основе где 0 являются. Второй проход по каждой строке уменьшает цифры на единицу, но заменяет 0 на ROWSPAN для каждой ячейки.

В этом решении также предполагается, что ВСЕ ячейки имеют атрибут ROWSPAN, даже если они охватывают только один. Если это не так, я могу добавить средства поддержки по умолчанию 1.

1 голос
/ 13 января 2012

Я думаю, что хорошей идеей было бы сохранить битовый вектор, который для каждого столбца содержит количество строк, до которых будет расширяться предыдущая ячейка в этом столбце. Для этого вам нужно использовать рекурсию, поскольку вам нужно изменить битовый вектор для каждой строки, а XSLT, поскольку функциональный язык программирования не имеет средств для изменения «переменных».

Следующая таблица стилей показывает идею таблицы с 3 столбцами.

<xsl:template match="TABLE2">
  <tbody>
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="ROW"/>
    </xsl:call-template>
  </tbody>
</xsl:template>

<xsl:template name="processRows">
  <xsl:param name="rows"/>
  <xsl:param name="index" select="1"/>
  <!-- Bit vector for the columns -->
  <xsl:param name="col1" select="0"/>
  <xsl:param name="col2" select="0"/>
  <xsl:param name="col3" select="0"/>

  <xsl:variable name="cellsBefore2">
    <xsl:choose>
      <xsl:when test="$col1 > 0">0</xsl:when>
      <xsl:otherwise>1</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="cellsBefore3">
    <xsl:choose>
      <xsl:when test="$col2 > 0">
        <xsl:value-of select="$cellsBefore2"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore2 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <row>
    <xsl:if test="$col1 = 0">
      <entry colname="1">
        <xsl:value-of select="$rows[$index]/CELL[1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col2 = 0">
      <entry colname="2">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore2 + 1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col3 = 0">
      <entry colname="3">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore3 + 1]/text()"/>
      </entry>
    </xsl:if>
  </row>
  <xsl:if test="$index &lt; count($rows)">
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="$rows"/>
      <xsl:with-param name="index" select="$index + 1"/>
      <xsl:with-param name="col1">
        <xsl:choose>
          <xsl:when test="$col1 > 0">
            <xsl:value-of select="$col1 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col2">
        <xsl:choose>
          <xsl:when test="$col2 > 0">
            <xsl:value-of select="$col2 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore2 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col3">
        <xsl:choose>
          <xsl:when test="$col3 > 0">
            <xsl:value-of select="$col3 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore3 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Как видите, проблема в том, что количество столбцов жестко задано. Если вы хотите иметь произвольное количество столбцов, вы должны иметь возможность генерировать и передавать список произвольной длины в качестве параметра. Это возможно только в XSLT 2.0. Однако, если вы используете XSLT 1.1, вы можете вместо этого использовать EXSLT (если он доступен в вашей среде), записать список как дерево результатов в переменную и использовать exslt: node-set () для этой переменной, чтобы получить пригодный для использования список .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...