Конвертировать ориентированные на столбцы данные в ориентированный на строки HTML - PullRequest
3 голосов
/ 29 января 2012

Мой исходный XML предоставляет данные столбец за столбцом; клетки имеют различную высоту. Используя XSLT 1.0, мне нужно повернуть его так, чтобы он соответствовал строковому формату таблицы HTML.

Пример: мне нужно преобразовать входные данные следующим образом

<source>
    <column>
        <cell height="1">col A row  1</cell>
        <cell height="2">col A rows 2-3</cell>
    </column>
    <column>
        <cell height="1">col B row  1</cell>
        <cell height="1">col B row  2</cell>
        <cell height="1">col B row  3</cell>
    </column>
    <column>
        <cell height="3">col C rows 1-3</cell>
    </column>
</source>

в таблицу HTML вот так

<table>
    <tr>
        <td rowspan="1">col A row  1</td>
        <td rowspan="1">col B row  1</td>
        <td rowspan="3">col C rows 1-3</td>
    </tr>
    <tr>
        <td rowspan="2">col A rows 2-3</td>
        <td rowspan="1">col B row  2</td>
    </tr>
    <tr>
        <td rowspan="1">col B row  3</td>
    </tr>
</table>

Как?

Редактировать: Вот еще один пример, в котором ни один столбец не имеет столько ячеек, сколько строк таблицы.

<source>
    <column id="A">
        <cell height="1">col A row  1</cell>
        <cell height="2">col A rows 2-3</cell>
    </column>
    <column id="B">
        <cell height="2">col B row  1-2</cell>
        <cell height="1">col B row  3</cell>
    </column>
    <column id="C">
        <cell height="3">col C rows 1-3</cell>
    </column>
</source>

в HTML-таблицу, подобную этой

<table>
    <tr>
        <td rowspan="1">col A row  1</td>
        <td rowspan="2">col B row  1-2</td>
        <td rowspan="3">col C rows 1-3</td>
    </tr>
    <tr>
        <td rowspan="2">col A rows 2-3</td>
    </tr>
    <tr>
        <td rowspan="1">col B row  3</td>
    </tr>
</table>

1 Ответ

5 голосов
/ 30 января 2012

I. XSLT 1.0 решение

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vrtfPass1">
  <xsl:apply-templates mode="pass1"/>
 </xsl:variable>

 <xsl:variable name="vPass1"
   select="ext:node-set($vrtfPass1)"/>

 <xsl:variable name="vMaxRow">
  <xsl:for-each select=
    "$vPass1/*/*/cell/@startRow">
    <xsl:sort select="." data-type="number" order="descending"/>

    <xsl:if test="position() = 1">
     <xsl:value-of select="."/>
    </xsl:if>
  </xsl:for-each>
 </xsl:variable>

 <xsl:template match="node()|@*" mode="pass1">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="pass1"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="cell" mode="pass1">
  <cell height="{@height}"
   startRow="{sum(preceding-sibling::*/@height) +1}">
   <xsl:copy-of select="text()"/>
  </cell>
 </xsl:template>

 <xsl:template match="/">
  <table>
    <xsl:call-template name="makeRows">
     <xsl:with-param name="pmaxRow" select="$vMaxRow"/>
    </xsl:call-template>
  </table>
 </xsl:template>

 <xsl:template name="makeRows">
  <xsl:param name="prowNum" select="1"/>
  <xsl:param name="pmaxRow" select="1"/>

  <xsl:if test="not($prowNum > $pmaxRow)">
    <tr>
      <xsl:apply-templates select=
      "$vPass1/*/*/cell[@startRow = $prowNum]"/>
    </tr>

    <xsl:call-template name="makeRows">
     <xsl:with-param name="prowNum" select="$prowNum+1"/>
     <xsl:with-param name="pmaxRow" select="$pmaxRow"/>
    </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match="cell">
  <td rowspan="{@height}"><xsl:value-of select="."/></td>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<source>
    <column>
        <cell height="1">col A row  1</cell>
        <cell height="2">col A rows 2-3</cell>
    </column>
    <column>
        <cell height="2">col B row  1-2</cell>
        <cell height="1">col B row  3</cell>
    </column>
    <column>
        <cell height="3">col C rows 1-3</cell>
    </column>
</source>

дает желаемый, правильный результат :

<table>
   <tr>
      <td rowspan="1">col A row  1</td>
      <td rowspan="2">col B row  1-2</td>
      <td rowspan="3">col C rows 1-3</td>
   </tr>
   <tr>
      <td rowspan="2">col A rows 2-3</td>
   </tr>
   <tr>
      <td rowspan="1">col B row  3</td>
   </tr>
</table>

II. Решение XSLT 2.0 :

Это похоже на решение XSLT 1.0, но мы используем функцию max() и также избегаем рекурсии с помощью оператора to.

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vPass1">
  <xsl:apply-templates mode="pass1"/>
 </xsl:variable>

 <xsl:variable name="vMaxRow" as="xs:integer"
  select="max($vPass1/*/*/cell/@startRow/xs:integer(.))"/>

 <xsl:template match="node()|@*" mode="pass1">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="pass1"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="cell" mode="pass1">
  <cell height="{@height}"
   startRow="{sum(preceding-sibling::*/@height) +1}">
   <xsl:copy-of select="text()"/>
  </cell>
 </xsl:template>

 <xsl:template match="/">
  <table>
    <xsl:for-each select="1 to $vMaxRow">
      <tr>
        <xsl:apply-templates select=
          "$vPass1/*/*/cell[@startRow = current()]"/>
      </tr>
    </xsl:for-each>
  </table>
 </xsl:template>

 <xsl:template match="cell">
  <td rowspan="{@height}"><xsl:value-of select="."/></td>
 </xsl:template>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...