随着B/S系统的普及和XML技术的深入应用,越来越多的数据包裹着XML的外衣被存储和扔来扔去。这些数据很多源自数据库,但经过一定的处理,数据更精简,更贴近应用。如果报表工具能利用这些XML数据,则可以减少数据库查询和报表数据运算操作,因为这些XML数据的始作俑者已经完成了这些操作。因此说XML是报表数据的新大陆。
传统的报表工具都是只能处理关系型数据库,基本上不能处理其他数据。随着时代的发展,一些报表工具加上了处理XML文档的能力,但需要编程,编写并配置插件,如此报表程序结构复杂,接口众多,用户还需要根据各种规范动手编写程序,若XML文档格式有很多种,就需要编写很多种的插件,报表开发量不小。
如果实现了一种能处理XML文档,从中获得报表数据的通用方法。则在定制报表模板的时候就教育报表引擎如何从XML文档获得数据,则对于所有的或大部分的XML文档无需编程,可减少报表开发量。
那么如何通用的处理具有复杂树状结构的XML文档呢?
大家知道,处理XML文档有两种模式,一个是DOM方式,另一个是流模式。DOM方式处理方便,但速度慢耗内存,流模式处理速度快,省内存但使用不方便。对于.NET平台,使用System.Xml.XmlDocument对象来使用DOM方式处理XML文档,而使用XmlReader 来使用流模式处理XML文档。
W3C国际标准组织设计XML文档的目标是方便的存储和交换小型数据包,而不考虑数据的冗余。因此若实际系统中出现巨大的XML文档,则大多数情况下可以认为是不恰当的使用XML技术。因此我认为报表工具不应当处理巨大的XML文档,在此前提下,为了实现方便,采用DOM方式来处理XML文档。
在.NET中使用XmlDocument加载一个XML文档后,会形成一个以XmlDocument为根节点的XML对象树状结构,面对这个XML树结构,获取其中的数据方法很自然的就是使用XPath技术了。XPath技术就是在XML树状结构中,以某个节点作为起始节点,使用特定的描述表示的路径移动到其他的节点,一般的是向其下层节点移动,比如移动到某个子节点或孙节点,移动到某个属性等等。
传统的报表数据源模型是两层的,即使扩展到可以处理XML文档也只能处理一次,既从根节点出发,使用某个XPath 获得字段的值。如此只能处理一次,处理完毕后XML就被扔掉了。但很多时候需要对XML文档进行进一步处理。此时传统的两层报表数据源模型是不够的。
为了能对XML文档进行精耕细作,需要从传统的两层数据源结构突破到多层的报表数据源模型。在多层数据源节点中,每一个节点都映射到XML文档中的某个节点,而它的子节点则使用XPath路径映射到XML中的其他节点。如此递归循环后则多层的数据源可以映射到XML文档中的任意节点。多层数据源模型实际上就是一个数据源树,因此处理XML文档也就是将两颗树在某些节点上钉在一起,而XPath就是钉子。因此需要注意各级数据源节点的XPath配置的连续性,若一个数据源节点的XPath设置不对,就像它钉错了XML节点或者干脆钉在空处。则它本身和所有的子孙节点都都会绑定落空的。
在实际应用中,由于XML文档不是专门为报表而产生的,因此报表工具还可能要离开XML文档来获取更多的报表数据,此时可能需要从XML文档跳到其他XML文档,或者返回数据库继续执行SQL查询。这就非常考验报表数据源模型的灵活性了。
大家都知道RSS文档是一种XML文档,在这里使用博客园的RSS文档为例子说明从XML读取报表数据的过程。首先考察一下RSS文档的结构,博客园首页的RSS文档URL为 http://www.cnblogs.com/rss.aspx ,根节点为 rss , 然后有个 channel 子节点,下面包含了RSS文档的基本信息,然后有若干个item节点,列出了所有文章的基本信息。item节点下面是文章的基本信息,其中 wfw:commentRss 子节点的内容是针对该文章回帖信息RSS文档的URL。可以根据这个URL加载的回帖信息RSS文档,根据RSS文档结构可以定义出如下的报表数据源和RSS文档的映射关系。
这里的RSSXML文档有三层结构,而且需要动态加载XML文档进行更深入的处理,因此传统的两层结构是肯定不够的,必须采用多层次的报表数据源结构。其过程是相当复杂的,步骤为
- 加载 http://www.cnblogs.com/rss.aspx 处的XML文档,作为主XML文档,生成一个System.Xml.XmlDocument 对象,并以该XML文档对象作为处理的出发点。
- 使用XPath"rss"遍历所有符合该路径的XML节点,很显然只处理了一个节点,此时当前位置就移动到rss节点。
- 从当前节点处使用XPath"channel/title"获得网站标题,使用"channel/link"获得网站地址,"channel/description"获得网站说明,"channel/pubDate"获得文档发布时间。
- 从当前节点处使用"channel"遍历所有符合该路径的XML节点,很显然只处理了一个节点,此时当前位置就移动到了"channel"节点。
- 变量当前节点下的所有的item子节点,并依次设置为当前节点。
- 从当前节点处使用“title"获得文章标题,使用"link"获得文章地址,使用”author"获得作者,“pubDate"获得发布时间,"description"获得文章内容,"slash:comment"获得回复数,“wfw:commentRss"获得回复RSSXML文档的URL。
- 当处理"wfw:commentRss"节点时,程序根据某个特定的设置加载该节点数据指向的XML文档,也就是加载针对当前文章的回复RSSXML文档。并遍历刚刚加载的XML文档的所有符合"rss/channel/item"的节点,并依次设置为当前节点。
- 从当前节点处,使用"author"获得回复作者,使用”pubDate"获得回复时间,使用"description"获得回复内容。
- 由于RSSXML文档中的description节点处保存的时HTML代码,因此还需要解析HTML代码并提取其中的纯文本内容。
从上面的步骤可以看出,数据源结构中的每一个节点都钉到了XML文档中的某个节点,而且在回复列表这个节点中,程序执行了XML文档的跳转,从主XML文档跳到回复RSSXML文档,而且是处理树状结构的,因此是递归操作,很多状态信息都由系统调用堆栈自动保存,无需程序自己保存了。
如果报表程序能直接连接到博客园数据库的话,还可以从文章作者这个节点执行XML文档到数据库的跳转,直接查询数据库,获得文章作者的一些注册信息。实事上,这个数据源树状结构中每一个节点都可以发生XML到XML,XML到数据库,数据库到XML的三种跳转,这大大扩展了获取报表数据的灵活性。
如果一个信息系统是纯XML应用的话,则报表工具就可以在众多的XML文档中跳跃着采集数据而不需要查询数据库,就像少林寺的武僧在梅花桩上打架而不用碰地,因此也就不用管下面是JAVA土壤的还是.NET土壤。此时所有的数据库操作,业务逻辑等等都运行在后台,而报表工具无需关心,只要系统底层安全可靠,则报表模块也就安全可靠,系统底层无论如何修改,只要XML文档格式不变则报表模块就不需要修改。对于非常复杂的报表数据源,超出了报表工具的定制能力,则可以编程提供一个XML文档供报表程序使用。以前系统是直接通过API向报表程序提供复杂报表数据,而现在通过XML文档以“隔山打牛”的方式向报表程序提供复杂报表数据。此时系统结构更安全,边界更清楚,体现了XML WebService的指导思想。这种获取数据的思想可以不局限于报表数据的获取,还可以应用到其他领域。
以上讨论的应用系统限于B/S系统,但可以设想,C/S系统经过改造,可以通过某种方式向报表工具提供XML数据文档。
从上面的讨论可以看出,XML确实是报表数据的新大陆,本人写的报表工具已经初步体现了这种思想。但这种思想可能有点激进,还不成熟不完善,希望大家多多指点。
XDesigner 软件工作室 2006-8-30