首页 > 解决方案 > XSLT 1.0 - 使用基于单独 XML 元素的两个表构建 XML 电子表格

问题描述

我需要从包含以单独元素结构化的数据的 XML 文档(使用 XSLT 1.0)以某种格式构建 XML 电子表格 (SpreadsheetML) 文件。一个简化的示例如下:

<Root>
<DetailsTable>
    <row data="1"/>
    <row data="2"/>
    <row data="3"/>
    <row data="4"/>
    <row data="5"/>
    <row data="6"/>
    <row data="7"/>
    <row data="8"/>
    <row data="9"/>
    <row data="10"/>
    <row data="11"/>
    <row data="12"/>
    <row data="13"/>
    <row data="14"/>
</DetailsTable>

<SummaryTable>
    <row data="A"/>
    <row data="B"/>
    <row data="C"/>
</SummaryTable>    
</Root>

所需的输出应该在 Excel 工作表上布置为两个单独的“表格”,其中一个位于另一个的中间 - 如屏幕截图所示:

在此处输入图像描述

问题是,通过 XSLT 转换,我需要逐一打印行,并且必须在打印行时打印所有单元格 - 因此,对于某些行(例如索引 4、5、6、7),我需要添加一些特殊的单元格,我将从SummaryTableXML 元素中获取它们的值。

换句话说,输出 XML 格式是这样的:

    <Table>
   <Row>
    <Cell><Data >DetailsTable</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >1</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >2</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >3</Data></Cell>
    <Cell><Data >SummaryTable</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >4</Data></Cell>
    <Cell><Data >A</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >5</Data></Cell>
    <Cell><Data >B</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >6</Data></Cell>
    <Cell><Data >C</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >7</Data></Cell>
   </Row>
   <Row>
    <Cell><Data >8</Data></Cell>
   </Row>
   <Row>
</Table>

问题是我无法控制详细信息表中的行数 - 只需对它们执行 'for-each' 并使用position().

然而,可能是太少(例如只有一个行元素),在这种情况下for-each永远不会迭代到索引 4、5、6、7,所以我的汇总表不会被打印 - 这种情况下的预期输出应该是:

在此处输入图像描述

我正在考虑执行 for-each 循环指定次数(例如 7 次),以便我打印出前七行以及汇总表,然后for-each在元素上再次执行循环DetailsTable/row,但跳过前 7 个索引位置,但这似乎是一个令人讨厌的解决方法。

还有其他想法吗?

标签: xmlxsltspreadsheetml

解决方案


一种解决方案,虽然看起来不是很优雅,但是有一个递归模板,它递增一个行号,直到它到达详细行或摘要行的末尾,这取决于哪个更大。

试试这个 XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:param name="firstSummaryRow" select="4" />

  <xsl:variable name="lastDetailRow" select="1 + count(/*/DetailsTable/row)" />
  <xsl:variable name="lastSummaryRow" select="$firstSummaryRow + count(/*/SummaryTable/row)" />

  <xsl:template match="/*">
    <xsl:call-template name="Rows" />
  </xsl:template>

  <xsl:template name="Rows">
    <xsl:param name="rowNumber" select="1" />
    <Row>
      <Cell>
        <Data>
          <xsl:choose>
            <xsl:when test="$rowNumber = 1">Details Table</xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="DetailsTable/row[position() = $rowNumber - 1]/@data" />
            </xsl:otherwise>
          </xsl:choose>
        </Data>
      </Cell>
      <xsl:if test="$rowNumber >= $firstSummaryRow and $rowNumber &lt;= $lastSummaryRow">
        <Cell Index="3">
          <Data>
            <xsl:choose>
              <xsl:when test="$rowNumber = $firstSummaryRow">Summary Table</xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="SummaryTable/row[position() = $rowNumber - $firstSummaryRow]/@data" /> 
              </xsl:otherwise>
            </xsl:choose>
          </Data>
        </Cell>
      </xsl:if>
    </Row>
    <xsl:if test="$rowNumber &lt; $lastDetailRow or $rowNumber &lt; $lastSummaryRow">
      <xsl:call-template name="Rows">
        <xsl:with-param name="rowNumber" select="$rowNumber + 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

在http://xsltfiddle.liberty-development.net/pPzifpd上查看它的实际应用


推荐阅读