XML/XSLT Word 报表生成器






3.80/5 (6投票s)
该工具基于 XML/XSLT,允许用户从头开始创建 Word 报表,即:构造 SQL 查询,构造 WordML 模板,生成文档。
目录
- 引言
- XSLT 简介
- 报表生成
- SQL -> XML
- XML -> XML schema
- XML schema -> WordML (基于 XML schema 的 WordML 模板创建)
- WordML -> XSLT (WordML 模板到 XSLT 转换)
- 将 XSLT 转换应用于 XML 数据
- 结论
引言
许多工具允许基于数据库中的数据创建文档: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 ->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>
结论
这样,我们就得到了一个填充了数据库数据的 Word 文档。我不得不提的是,我使用了 SqlWrapper - 一个非常合适的数据访问层。我很乐意回答您的所有问题。