IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

技巧: 使用基于拉的 DOM

在容易和高效的编程之间找到平衡点

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Uche Ogbuji (uche.ogbuji@fourthought.com), 首席顾问, Fourthought,Inc.

2002 年 5 月 01 日

XML 应用程序开发人员经常必须应付 SAX 的复杂性或 DOM 的低效率。这篇技巧文章展示了 DOM 的拉方法是如何通过提供简单、高效的解析来高效地跨过这两者之间的鸿沟。

用于将 XML 解析绑定到需要它的应用程序的两个最常见系统是 W3C 的文档对象模型(Document Object Model (DOM))和开放社区的标准 ― 用于 XML 的简单 API(Simple API for XML (SAX))。DOM 是一种允许代码直接指向 XML 文档各部分特性的 API,因此易于编程。然而,DOM 通常要求将表示文档每个部分的对象都放入内存中。因为这些对象占用内存的总合可能会多达文档本身所占用内存的 10 倍(或更多),因此 DOM 在处理大文档时的效率很低。SAX 逐位遍历文档树并发送出与当前节点相应的事件。这意味着 SAX 可以抛弃暂时不在范围中的文档部分,这使得它更高效。DOM 是一种允许代码直接读和修改 XML 文档各部分特性的 API,因此易于编程。

为了赋予开发人员 DOM 的容易和 SAX 的效率,有许多项目着重于各种仅在请求 XML 文档某部分时才将其装入的 DOM。这些 API 称为 拉 DOM(pull DOM)

解析还是不解析……

我将提供一个 Python 语言标准库中的示例。我认为,即使您不熟悉 Python,也很容易理解这个示例。Python 的最新版本捆绑了几个 XML 工具,包括一个小的 DOM 实现 minidom 和一个 SAX 库。Python 还提供了一个 pull DOM(在 xml.dom.pulldom 模块中),我将演示这个pull DOM。 清单 1演示了使用 Python 的pull DOM 装入莎士比亚的“哈姆雷特”的著名 Jon Bosak XML 表示法的用法。任务是打印该剧的第 IV 幕第 II 场的第一行。


清单 1:打印“哈姆雷特”第 IV 幕第 II 场的第一行
       1	#Get the first line in Act IV, scene II
     2	
     3	from xml.dom import pulldom
     4	
     5	hamlet_file = open("hamlet.xml")
     6	
     7	events = pulldom.parse(hamlet_file)
     8	act_counter = 0
     9	for (event, node) in events:
    10	    if event == pulldom.START_ELEMENT:
    11	        if node.tagName == "ACT":
    12	            act_counter += 1
    13	            scene_counter = 1
    14	        if node.tagName == "SCENE":
    15	            if act_counter == 4 and scene_counter == 2:
    16	                events.expandNode(node)
    17	                #Traditional DOM processing starts here
    18	                #Get all descendant elements named "LINE"
    19	                line_nodes = node.getElementsByTagName("LINE")
    20	                #Print the text data of the text node
    21	                #of the first LINE element
    22	                print line_nodes[0].firstChild.data
    23	            scene_counter += 1

首先,描述一下 hamlet.xml 的大概结构。顶级元素是 PLAY ,它包含了许多 ACT 元素和其它元素, ACT 元素又包含了许多 SCENE 元素。 SCENE 包含 SPEECH ,而 SPEECH 又包含一组 LINE ,每个 LINE 由一个演员来讲。这是一个相当简单的层次结构。

在第 3 行中导入库以后,我在第 5 行中打开了 XML 文件并对其解析进行了初始化。 pulldom 解析返回一个对象,该对象表示来自该文件的所有解析事件的虚集合。在 9-23 行中,我在这个集合上进行了循环。循环中的每次迭代取回一个事件和一个虚节点,它潜在地表示以该虚节点为根的整个子树。您可以检查它是什么类型的事件 ― 含蓄地说,是什么类型的节点 ― 以及该节点表面的一些东西,如节点名称。如果您想要关于其子节点的信息,可以等待适当的后续事件或使用 expandNode 方法将该节点展开成其完整的实际 DOM 树。

在第 10 行,我检查当前事件是否元素的开始,这是我在该程序的拉部分关心的唯一事件类型。如果它是 ACT 元素(在第 11 行检查),则我更新此类元素的计数器(在第 8 行初始化),并使 SCENE 元素计数器复位。如果它是 SCENE 元素(在第 14 行检查),则我检查它是否我想要的幕和场号,如果不是,则更新计数器。

如果是我想要的那一幕,则我将如上面所提到的那样,用 expandNode 将该幕的整个 DOM 结构拉到内存中。从此时开始,该节点是常规 DOM 节点,您可以在节点上调用常规 DOM 方法。在第 19 行中,我使用 getElementsByTagName DOM 方法来获得所有名为 LINE 的子孙元素。理解这一点很重要:如果我在第 16 行之前调用了该方法,它将导致一个错误;这是因为,在展开节点之前,没有真正的 DOM 树。理解这一点也很重要:您选择在哪个节点上进行展开决定了产生的效率。如果我选择了展开整个 ACT 而不是 SCENE ,则所有其它 SCENE 也都被装进内存。

最后,在抓取了第一个 LINE 元素之后,我在第 22 行中搜寻其文本子节点,并打印其内容。任务完成。





回页首


用您熟悉的语言就可以使用拉 DOM

这是一个使用 Python 的示例,但越来越多的语言开始拥有某种类型的拉 API。Perl 有 XML::Twig,遗憾的是,它不是基于 DOM 的,Java 社区正在标准化一个自己的拉 DOM API。拉 DOM 介于 SAX 和 DOM 之间,可以容易地处理任意大小的 XML 文档,不用费太多力气。



参考资料



关于作者

作者照片:Uche Ogbuji

Uche Ogbuji 是 Fourthought Inc.的顾问兼共同创始人,该公司是专为企业知识管理提供 XML 解决方案的软件供应商和咨询公司。Fourthought 开发了 4Suite,一个用于 XML、RDF 和知识管理应用程序的开放源码平台。Ogbuji 先生是一位出生于尼日利亚的计算机工程师和作家,他现在美国科罗拉多州博耳德(Boulder)生活和工作。可以通过 uche.ogbuji@fourthought.com与 Ogbuji 先生联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款