65.9K
CodeProject 正在变化。 阅读更多。
Home

XML/XSLT Word 报表生成器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (6投票s)

2009 年 1 月 11 日

CPOL

4分钟阅读

viewsIcon

126483

downloadIcon

2653

该工具基于 XML/XSLT,允许用户从头开始创建 Word 报表,即:构造 SQL 查询,构造 WordML 模板,生成文档。

目录

引言

许多工具允许基于数据库中的数据创建文档:Crystal Reports、Fast Reports 等。大多数工具不允许用户编辑模板。我试图解决的任务是创建一个基于 XML/XSLT 的工具,该工具允许用户从头开始创建模板,即:

  • 构造一个 SQL 查询,以便从 SQL Server 获取 XML 和 XML schema。
  • 构造一个 WordML 模板。
  • 生成一个文档,该文档可以被视为一个 WordML 模板,其中填充了从 SQL Server 获取的数据。

“XML/XSLT”方法的优势在于用户有机会创建具有相当复杂拓扑结构的树。Word 之所以被选作用于构造 WordML 模板的工具,是因为其通用性和显示 XML 结构的能力。基于 XML/XSLT 实现生成器的想法是在阅读了文章 Generating Word Reports/Documents 后产生的。我强烈建议您阅读这篇文章。

XSLT/XSL 简介

使用 XSL,您可以自由修改任何源文本 (XML),并从同一个源文件生成不同的输出。XSL 处理器解析 XML 源并尝试查找匹配的模板规则。如果找到,则会评估匹配模板内的指令。模板应应用于的 XML 文档部分由位置路径确定。所需语法在 XPath 规范中指定。简单情况看起来非常像文件系统寻址。处理始终从模板 match ="/" 开始。这匹配根节点(节点,其唯一的元素子节点是文档元素,在本例中为“root”)。许多样式表不显式包含此元素。当此模板未显式给出时,将使用隐式模板(它包含唯一的指令)。此指令意味着:处理当前节点的所有子节点,包括文本节点。当节点存在模板时,不会调用默认处理。如果您想包含节点的后代,则必须显式请求它们的模板。

报表生成

报表生成可以大致分为四个步骤。

  • 构造一个 SQL 查询,以便获取 XML 数据和 XML schema,
  • 构造一个 WordML 模板,
  • 将 WordML 模板转换为 XSLT 转换,
  • 通过将 XSLT 转换应用于 XML 数据来生成报表。

SQL - > XML

SQL 查询是所有转换的起点。它决定了应从服务器请求的数据。我使用 MSSQL 2005 (Yukon) 服务器,该服务器提供了将请求的数据以 XML 格式返回的机会。典型的 SQL 查询如下所示:

WITH XMLNAMESPACES(DEFAULT 
    'http://wrg/kpd_types.xsd')
SELECT *
FROM kpd_types
FOR XML PATH('kpd_types'), ROOT('root')

服务器的响应

<root xmlns="http://wrg/kpd_types.xsd">
<kpd_types>
    <oid>1</oid>
    <category>cargo</category>
    <label>paper</label>
</kpd_types>
<kpd_types>
    <oid>2</oid>
    <category>cargo</category>
    <label>food</label>
</kpd_types>
<kpd_types>
    <oid>3</oid>
    <category>cargo</category>
    <label>metal</label>
</kpd_types>
</root>

XML->XML schema

什么是 XML schema?XML schema 是对 XML 结构的描述。简而言之,它包含 XML 文档中所有元素和属性的枚举。当我们开始构造 WordML 模板时,我们将需要它。我们可以放置在 WordML 文档中的元素集合将由 XML schema 确定。

让我们考虑 DataSet 类。它有两个有用的方法:ReadXml()WriteXmlSchema()。当调用 ReadXml() 方法时,XML 结构会在后台自动构建。我们可以通过 WriteXmlSchema() 方法读取或存储它。

XmlDataDocument doc = new XmlDataDocument();
XmlReader xmlData;
xmlData = XmlReader.Create(new StringReader(xml));
doc.DataSet.ReadXml(xmlData);
doc.DataSet.WriteXmlSchema(WordReportGenerator._path + "//" + 
                           _xmlSchemaFileName + ".xsd");

XML schema -> WordML

当我们有了 XML schema 后,就可以创建报表模板了。首先,我们必须将其附加到 Word 文档。

object schema = _xmlSchemaName;
object alias = _xmlSchemaAlias;
object schemafilename = _path + _xmlSchemaFileName + ".xsd";
App.ActiveDocument.XMLSchemaReferences.Add(ref schema, 
                   ref alias, ref schemafilename, true);

然后,进行一些调整:使 XML 标记和任务窗格可见,并允许在不验证的情况下保存文档。

App.ActiveWindow.View.ShowXMLMarkup =true;
App.TaskPanes[Word.WdTaskPanes.wdTaskPaneXMLStructure].Visible =true;
App.ActiveDocument.XMLSchemaReferences.AllowSaveAsXMLWithoutValidation = true;

WordML.JPG

WordML ->XSLT

让我们看看模板如何转换为 XSLT。初始 WordML 模板可以简写为:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument 
xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:ns0="http://wrg/kpd_types.xsd">
…
<w:body><wx:sect>
…
<ns0:root>
    <ns0:kpd_types>
    <w:p>
        <ns0:oid/>
        <w:r><w:t>,</w:t></w:r>
        <ns0:category/>
        <w:r><w:t>,</w:t></w:r>
        <ns0:label/>
    </w:p>
    </ns0:kpd_types>
</ns0:root>
…
</wx:sect></w:body>
</w:wordDocument>

其中 <ns0:root></ns0:root> 之间的标签本身就是用于填充 XML 数据的表单。XSTL 转换提供了这种填充机制。让我们看看它是什么样的。

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
xmlns:ns0="http://wrg/kpd_types.xsd">

<xsl:output method="xml" encoding="UTF-8" standalone="yes"/>

<xsl:template match="/">
<xsl:processing-instruction name="mso-application">
<xsl:text>progid="Word.Document"</xsl:text>
</xsl:processing-instruction>

<w:wordDocument 
xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" 
xmlns:o="urn:schemas-microsoft-com:office:office" 
xmlns:ns0="http://wrg/kpd_types.xsd">
…
<w:body><wx:sect>
…
<xsl:apply-templates select="ns0:root" />
…
</wx:sect></w:body>
…
</w:wordDocument>
</xsl:template>

<xsl:template match="/ns0:root">
<ns0:root>
    <xsl:apply-templates select="ns0:kpd_types" />
</ns0:root>
</xsl:template>

<xsl:template match="/ns0:root/ns0:kpd_types">
<ns0:kpd_types>
<w:p>
    <xsl:apply-templates select="ns0:category"/>
        <w:r><w:t><xsl:text>,</xsl:text></w:t></w:r>
    <xsl:apply-templates select="ns0:label"/>
        <w:r><w:t><xsl:text>,</xsl:text></w:t></w:r>
    <xsl:apply-templates select="ns0:oid" />
</w:p>
</ns0:kpd_types>
</xsl:template>

<xsl:template match="/ns0:root/ns0:kpd_types/ns0:label">
<ns0:label>
    <w:r><w:t><xsl:value-of select="." /></w:t></w:r>
</ns0:label>
</xsl:template>

<xsl:template match="/ns0:root/ns0:kpd_types/ns0:oid">
<ns0:oid>
    <w:r><w:t><xsl:value-of select="." /></w:t></w:r>
</ns0:oid>
</xsl:template>

<xsl:template match="/ns0:root/ns0:kpd_types/ns0:category">
<ns0:category>
    <w:r><w:t><xsl:value-of select="." /></w:t></w:r>
</ns0:category>
</xsl:template>

</xsl:stylesheet>

将 XSLT 转换应用于 XML 数据

假设我们有以下数据集作为源 XML:

<root xmlns="http://wrg/kpd_types.xsd">
<kpd_types>
    <oid>1</oid>
    <category>cargo</category>
    <label>paper</label>
</kpd_types>
<kpd_types>
    <oid>2</oid>
    <category>cargo</category>
    <label>food</label>
</kpd_types>
<kpd_types>
    <oid>3</oid>
    <category>cargo</category>
    <label>metal</label>
</kpd_types>
</root>

将 XSLT 转换应用于此 XML,我们得到最终文档:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument 
  xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" 
  xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" 
  xmlns:o="urn:schemas-microsoft-com:office:office" 
  xmlns:ns0="http://wrg/kpd_types.xsd">
  …
<w:body><wx:sect>
…
<ns0:root>
    <ns0:kpd_types>
    <w:p>
        <ns0:category><w:r><w:t>cargo</w:t></w:r></ns0:category><w:r><w:t>,</w:t></w:r>
        <ns0:label><w:r><w:t>paper</w:t></w:r></ns0:label><w:r><w:t>,</w:t></w:r>
        <ns0:oid><w:r><w:t>1</w:t></w:r></ns0:oid>
    </w:p>
    </ns0:kpd_types>
    <ns0:kpd_types>
    <w:p>
        <ns0:category><w:r><w:t>cargo</w:t></w:r></ns0:category><w:r><w:t>,</w:t></w:r>
        <ns0:label><w:r><w:t>food</w:t></w:r></ns0:label><w:r><w:t>,</w:t></w:r>
        <ns0:oid><w:r><w:t>2</w:t></w:r></ns0:oid>
    </w:p>
    </ns0:kpd_types>
    <ns0:kpd_types>
    <w:p>
        <ns0:category><w:r><w:t>cargo</w:t></w:r></ns0:category><w:r><w:t>,</w:t></w:r>
        <ns0:label><w:r><w:t>metal</w:t></w:r></ns0:label><w:r><w:t>,</w:t></w:r>
        <ns0:oid><w:r><w:t>3</w:t></w:r></ns0:oid>
    </w:p>
    </ns0:kpd_types>
</ns0:root>
…
</wx:sect></w:body>
…
</w:wordDocument>

ReportXML.JPG

Report.JPG

结论

这样,我们就得到了一个填充了数据库数据的 Word 文档。我不得不提的是,我使用了 SqlWrapper - 一个非常合适的数据访问层。我很乐意回答您的所有问题。

© . All rights reserved.