假设你有这样一个文件:
<companies name="My Companies">
<company>
<companyName>Cool Beans</companyName>
<serviceID>1</serviceID>
<startDate>01-01-2014 00:00:00</startDate>
<endDate>01-02-2014 00:00:00</endDate>
<hours>2</hours>
</company>
<company>
<companyName>Hot Beans</companyName>
<serviceID>2</serviceID>
<startDate>01-01-2014 00:00:00</startDate>
<endDate>01-02-2014 00:00:00</endDate>
<hours>2</hours>
</company>
<company>
<companyName>Evil Beans</companyName>
<serviceID>3</serviceID>
<startDate>01-03-2014 00:00:00</startDate>
<endDate>01-04-2014 00:00:00</endDate>
<hours>2</hours>
</company>
</companies>
您必须生成有效的 XLS 或 XSLX 文件。我将使用 [this Office 2003] (http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats) 格式作为示例 (XLS)。
您的样式表必须声明您可能需要的所有前缀和命名空间,以保证电子表格中的属性和元素的质量。您可以简单地在 XSLT 中声明它们,它们将被复制到您的结果文件中:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"> ... </xsl:stylesheet>
您需要生成一个mso-application 处理指令,它应该出现在文档根元素之前。我们可以为此创建一个模板:
<xsl:template match="/">
<xsl:processing-instruction name="mso-application">progid="Excel.Sheet"</xsl:processing-instruction>
<Workbook>
<xsl:apply-templates select="companies" />
</Workbook>
</xsl:template>
此模板将被处理一次,因为它与根匹配。它将调用companies 元素,该元素将处理样式表的其余部分。这是一个最小的模板。您也可以在那里放置元数据标签、样式等。
我们也可以将工作表代码放在根模板中。我决定将其分开,以避免使用大模板。这个也只会被处理一次,因为只有一个companies 节点。它将创建WorkSheet、Table 并调用其他一些模板来处理各个行和单元格。
<xsl:template match="companies">
<Worksheet ss:Name="{@name}">
<Table x:FullColumns="1" x:FullRows="1">
<Row><!-- Header Row -->
<xsl:apply-templates select="company[1]/*" mode="headers"/>
</Row>
<xsl:apply-templates select="company" />
<Row><!-- Last Row -->
<Cell ss:Index="4"><Data ss:Type="String">Total:</Data></Cell>
<Cell ss:Formula="=SUM(R[-{count(company)}]C:R[-1]C)">
<Data ss:Type="Number"></Data>
</Cell>
</Row>
</Table>
</Worksheet>
</xsl:template>
第一个Row 将包含标题。由于 XML 源没有标题名称,我们将使用第一个 company 的子元素 names 创建它们(在单独的模板中完成)。单独的行和单元格也将在单独的模板中处理,但这里我们创建 last 行。我们在第 4 列放置一个单元格以打印文本“Total:”,并在 以下 行中插入一个 Excel 公式,该公式将对前面的 n 行求和,其中 n 是 company 节点总数(R(-n)C:R(-1)C 将评估为 E2:E4 并读取为:“来自 这一行 - count(company) 到 这一行 - 1")。
其他模板使用ss:Type 信息为每一行和数据单元格创建代码(您要求和的字段必须是Number 类型)。
这是完整的样式表:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:processing-instruction name="mso-application">progid="Excel.Sheet"</xsl:processing-instruction>
<Workbook>
<xsl:apply-templates select="companies" />
</Workbook>
</xsl:template>
<xsl:template match="companies">
<Worksheet ss:Name="{@name}">
<Table x:FullColumns="1" x:FullRows="1">
<Row><!-- Header Row -->
<xsl:apply-templates select="company[1]/*" mode="headers"/>
</Row>
<xsl:apply-templates select="company" />
<Row><!-- Last Row -->
<Cell ss:Index="4"><Data ss:Type="String">Total:</Data></Cell>
<Cell ss:Formula="=SUM(R[-{count(company)}]C:R[-1]C)">
<Data ss:Type="Number"></Data>
</Cell>
</Row>
</Table>
</Worksheet>
</xsl:template>
<xsl:template match="company[1]/*" mode="headers">
<Cell>
<Data ss:Type="String">
<xsl:value-of select="name()" />
</Data>
</Cell>
</xsl:template>
<xsl:template match="company">
<Row>
<xsl:apply-templates select="*" />
</Row>
</xsl:template>
<xsl:template match="companyName|serviceID|startDate|endDate">
<Cell>
<Data ss:Type="String">
<xsl:value-of select="."/>
</Data>
</Cell>
</xsl:template>
<xsl:template match="hours">
<Cell>
<Data ss:Type="Number">
<xsl:value-of select="."/>
</Data>
</Cell>
</xsl:template>
</xsl:stylesheet>
将结果保存在具有.xls 扩展名(不是 .xslx)的文件中,然后在 Excel 中打开它。您将拥有一个电子表格,其中包含名为“我的公司”的工作表、表格列中的每个字段以及最后一行/列中的总小时数,以 Excel 公式计算。
这是一个 fiddle,其中包含应用于我在此答案开头提供的源代码(可能类似于您的源代码)的样式表。以下是结果列表:
<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Worksheet ss:Name="My Companies">
<Table x:FullColumns="1" x:FullRows="1">
<Row>
<Cell>
<Data ss:Type="String">companyName</Data>
</Cell>
<Cell>
<Data ss:Type="String">serviceID</Data>
</Cell>
<Cell>
<Data ss:Type="String">startDate</Data>
</Cell>
<Cell>
<Data ss:Type="String">endDate</Data>
</Cell>
<Cell>
<Data ss:Type="String">hours</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Cool Beans</Data>
</Cell>
<Cell>
<Data ss:Type="String">1</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-01-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-02-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="Number">2</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Hot Beans</Data>
</Cell>
<Cell>
<Data ss:Type="String">2</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-01-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-02-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="Number">2</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data ss:Type="String">Evil Beans</Data>
</Cell>
<Cell>
<Data ss:Type="String">3</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-03-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="String">01-04-2014 00:00:00</Data>
</Cell>
<Cell>
<Data ss:Type="Number">2</Data>
</Cell>
</Row>
<Row>
<Cell ss:Index="4">
<Data ss:Type="String">Total:</Data>
</Cell>
<Cell ss:Formula="=SUM(R[-3]C:R[-1]C)">
<Data ss:Type="Number"/>
</Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
这是在 Excel for Mac 2011 中加载后结果文件的屏幕截图:
单击位置 Row(5)Col(5) 或 E5) 处的字段计算总数,您应该看到它存储了 Excel 公式,该公式使用来自 Row(5-3)Col(5):Row(5-1)Col(5) (E2:E4) 的数据正确添加了 3 字段: