【问题标题】:Add Excel's SUM() to XLST result page?将 Excel SUM() 添加到 XSLT 结果页面?
【发布时间】:2014-05-27 20:18:37
【问题描述】:

如何将 SUM() 函数应用于每个 hours 节点?我将把这个转换后的 XML 保存为 Excel,我希望它嵌入 SUM() 函数。 经过研究,似乎有很多方法可以做到这一点,但没有一个真正适用于我的问题。

XSL:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <html>
            <body>
                <img src="../images/company_logo.png"></img>
                <p>Company: <xsl:value-of select="companies/company[1]/companyName"/></p>
                <p>Date: <xsl:value-of select="companies/company[1]/startDate"/> to <xsl:value-of select="companies/row[last()]/endDate"/></p>
                <table>
                    <xsl:for-each select="company/row">
                    <tr>
                        <td>ID:</td>
                        <td><xsl:value-of select="serviceID"/></td>
                        <td>Hours:</td>
                        <td><xsl:value-of select="hours"/></td>
                    </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

XML

<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>

【问题讨论】:

  • 您的源 XML 文档是什么样的?
  • 问题不清楚:您的样式表创建的是 HTML 表格,而不是 Excel 文档。 AFAIK,您不能在 HTML 文档中嵌入 Excel 函数 - 尽管您当然可以让样式表使用现有输入值计算总和并将其作为静态值放在结果中。
  • @michael.hor257k 我想将 XML 转换为 Excel。
  • 然后您应该创建一个原生 Excel 文档,例如 SpreadsheetML。看看这是否有助于您入门:stackoverflow.com/questions/2160159/…
  • 可以生成一个包含表格的 HTML 文件,将其扩展名更改为 .xsl 并将其作为 Excel 电子表格打开,但您不是t 真正创建一个真正的 XLSX 文件。 Excel 将生成一个。而且您将无法嵌入 Excel 公式。

标签: xml excel xslt xpath spreadsheetml


【解决方案1】:

假设你有这样一个文件:

<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 节点。它将创建WorkSheetTable 并调用其他一些模板来处理各个行和单元格。

<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 行求和,其中 ncompany 节点总数(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 字段:

【讨论】:

  • 嗯,我将生成的 XML 文件保存为 XLS,但每当我在 Excel 中打开它时,我都会得到:Problems came up in the following areas during load: Workbook setting。它还指向C:/Users/admin/AppData/Local/Mircosof/Windows/Temporary Internet Files/Content.MSO/25287435.log 中的一个日志文件,但我无法找到 Temporary Internet Files 文件夹。
  • 查看是否可以从该站点加载 Excel XML 文件:en.wikipedia.org/wiki/Microsoft_Office_XML_formats。我用它作为参考来生成您的示例(但我删除了一些我认为可能与此示例无关的元数据,并且因为它有效而将其遗漏了)。
  • 我应该提到我没有使用您的实际来源,因为您没有提供(您只提供了摘录)。我使用了我在这个答案开头提供的 XML 源。尝试用它生成电子表格。如果您的实际示例不同,您肯定需要对其进行调整才能正确生成电子表格。
  • 奇怪。我在 XML 中有相同的数据,只是我没有根节点的 name 属性。此外,每个company 节点内还有几个节点。我需要在 XLS 文件中列出它们吗?
  • 我正在使用工作表名称,这可能是必需的。我不确定。您可以向工作表添加静态名称:将 &lt;Worksheet ss:Name="{@name}"&gt; 替换为 &lt;Worksheet ss:Name="Sheet 1"&gt;
猜你喜欢
  • 1970-01-01
  • 2013-11-06
  • 2016-06-19
  • 2016-08-15
  • 2016-03-28
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 2010-11-10
相关资源
最近更新 更多