答案:文档对象模型
Eric Armstrong
SAX一章中已经编写了包含用于放映的幻灯片的XML文件。然后又使用SAX API将XML回送到显示器。
本章中,将使用文档对象模型(DOM)建立一个小的SlideShow应用程序。首先构建DOM并查看它,然后看看如何编写XML结构的DOM ,将它显示在GUI中,并且操作树结构。
文档对象模型是普通的树结构,每个节点包含一个来自于XML结构的组件。两种最常见的节点类型是元素节点(element node)和文本节点(text node)。使用DOM函数能够创建节点、删除节点、改变节点内容并且遍历节点层次。
本章将解析现有的XML文件以构建DOM,显示并查看DOM的层次结构,将DOM转换成能够友好显示的Jtree,并且展现命名空间的语法。你将从头开始创建一个DOM,并且了解如何使用Sun的JAXP实现的一些特定实现性能将现有的数据集转换成XML。
首先要确信,在你的应用程序中DOM是最好的选择。下一节何时使用DOM将具体介绍它。
--------------------------------------------------------------------------------
注意:可以在下面的页面中找到本章的例子<JWSDP_HOME>/docs/tutorial/examples/jaxp/dom/samples.
何时使用DOM
文档对象模型 (DOM) 是一个文档标准(例如,文章和书)。另外,JAXP 1.2实现支持XML Schema,在任何给定的应用程序中必须慎重考虑这一点。
另外,如果你正在处理简单数据结构,并且XML Schema不是你计划的一部分,那么你可能会发现一个面向对象的标准如JDOM 和dom4j 更加适合于实现你的目标。
DOM从一开始就是一种与语言无关的模型。这是因为它专门用于像C或Perl这类语言,没有利用Java的面向对象的性能。这个事实再加上文档/数据定义,也能帮助解释处理DOM和处理JDOM或dom4j结构之间的区别。
本节中,将讨论遵守这些标准的模型之间的区别,以便能够帮助你为应用程序选择最合适的模型。
文档vs.数据
DOM中使用的文档模型和JDOM或dom4j中使用的数据模型的主要区别在于:
· 层次结构中的节点类型
· “混合内容”的性能
正是构成数据层次结构的 “节点” 的不同造成了这两个模型之间编程的区别。然而,只有混合内容的性能能解释不同标准定义“节点”有何区别。所以,首先来看看DOM的“混合内容模型”。
混合内容模型
回想在对文档-驱动编程 (DDP) 的讨论中,可以将文本和元素自由结合到DOM层次结构中。这种结构被称为DOM模型中的“混合内容”。
文档中经常出现混合内容。例如,为了表示该结构:
<sentence>This is an <bold>important</bold> idea.</sentence>
DOM节点的层次结构看起来跟下面的很像,其中每行代表一个节点:
ELEMENT: sentence
+ TEXT: This is an
+ ELEMENT: bold
+ TEXT: important
+ TEXT: idea.
注意,句元素(sentence element)包含文本,后面跟着子元素,再后面是其他文本。 “混合内容模型”是通过将文本和元素混合起来而定义的。
节点种类
为了提供混合内容的性能,DOM节点生来就非常简单。例如,在前面的例子中,第一个元素的内容 (它的value)简单地标识了节点的类型。
DOM用户第一次通常都会被这个事实迷惑。到访过<sentence> 节点后,他们就想要节点的“内容”,并且想得到其他有用信息。但是,它们得到的是元素的名字“sentence”。
注意:DOM Node API定义了nodeValue()、node.nodeType()和nodeName() 方法。对于第一个元素节点,nodeName() 返回 "sentence",而nodeValue() 返回空。对于第一个文本节点, nodeName() 返回"#text",并且nodeValue() 返回"This is an "。很重要的一点是元素的value 和它的content不一样。
--------------------------------------------------------------------------------
处理DOM时,获得关心的内容意味着要检查节点包含的子元素列表,忽略那些不感兴趣的,只处理那些感兴趣的。
例如,在上面的例子中,如果要查询句子的“text(文本)”意味着什么?根据你的应用程序,下面的每一条都是合理的:
· This is an
· This is an idea.
· This is an important idea.
· This is an <bold>important</bold> idea.
简单模型
使用DOM可以方便地创建所需的语义。然而,也需要经过必要的处理,以实现这些语义。而像JDOM和dom4j这样的标准,使得事情更加简单,因为层次结构中的每个节点都是一个对象。
虽然JDOM 和dom4j 允许使用混合内容元素,但是它们主要不是用于这类情况的。相反,它们主要用于XML结构包含数据的应用程序中。
如传统数据处理中所介绍的,数据结构中的元素包含文本或其他元素,但是不能两个都包含。例如,下面的XML表示简单的通讯簿:
<addressbook>
<entry>
<name>Fred</name>
<email>fred@home</email>
</entry>
...
</addressbook>
--------------------------------------------------------------------------------
注意: 对于这类非常简单的XML数据结构,也可以使用Java平台1.4版本中的正则表达式包(java.util.regex)。
--------------------------------------------------------------------------------
在JDOM 和dom4j中,一旦到达包含文本的元素,就会调用一个方法如text() 来获得它的内容。处理DOM时,必须查看子元素列表,将节点的文本“放在一起”,就如前面所见到的——即便该列表只包含一项(TEXT 节点)。
所以,对于简单的数据结构(如前面的通讯簿),使用JDOM或dom4j可以节省很多工作。即便是数据在技术上是“混合”的,也可以使用这些模型中的某个模型,除非给定节点有一个(并且仅有一个)文本段。
下面是这类结构的一个例子,它在JDOM或dom4j中也很容易处理:
<addressbook>
<entry>Fred
<email>fred@home</email>
</entry>
...
</addressbook>
这里,每项都有标识文本,并且后面还有其他元素。使用该结构,程序能够浏览一个项,调用text() 来查看它属于谁,并且如果<email> 子元素在当前节点下,就处理它。
增加复杂度
但是要完全理解查找或操作DOM时需要进行的处理,就必需知道DOM包含的节点。
下面的例子主要就是针对这点的。它是该数据的一个表示法:
<sentence>
The &projectName; <![CDATA[<i>project</i>]]> is
<?editor: red><bold>important</bold><?editor: normal>.
</sentence>
该句子包含一个实体引用——一个在其他地方定义的指向“实体”的指针。这里,实体包含项目名。这个例子也包含CDATA段(未解释的数据,如HTML中的<pre> 数据),和处理指令 (<?...?>)。在本例中该处理指令告诉编辑器在绘制文本时使用哪种颜色。
下面是该数据的DOM结构。它代表了强壮的应用程序要处理的结构:
+ ELEMENT: sentence
+ TEXT: The
+ ENTITY REF: projectName
+ COMMENT: The latest name we're using
+ TEXT: Eagle
+ CDATA: <i>project</i>
+ TEXT: is
+ PI: editor: red
+ ELEMENT: bold
+ TEXT: important
+ PI: editor: normal
本例描绘了可能在DOM中出现的节点。虽然在绝大多数时候你的应用程序能够忽略它们中的大部分,但是真正强壮的实现需要识别并处理它们中的每一个。
同样,到访节点的处理涉及到子元素的处理,它忽略你不关心的元素仅仅查看关心的元素,直到找到感兴趣的节点。
通常,在这类情况下,仅对找到包含指定文本的节点感兴趣。例如,在DOM API 中,有一个例子,那个例子中希望找到一个<coffee> 节点,该节点的<name> 元素包含文本“Mocha Java”。要执行该查找,程序需要处理<coffee> 元素列表,并且对于每一个列表:a)获取它下面的<name> 元素。b)检查该元素下的TEXT 节点。
然而,该例是建立在一些简单的假设之上的。它假设处理指令、注释、CDATA节点和实体引用不能存在于数据结构中。许多简单的应用程序能够去掉这些假设。另外,真正强壮的应用程序需要处理各类有效的XML数据。
(仅当输入数据包含所需的简化的XML结构时,“简单”的应用程序才能运行。但是没有任何验证机制能够确保不存在更加复杂的结构。毕竟,XML是专门为它们设计的。)
要变得更加强壮,DOM API中的例子代码必须完成以下事情:
1. 搜索<name> 元素时:
a. 忽略注释、属性和处理指令。
b. 允许出现<coffee> 子元素没有按指定的顺序出现的情况。
c. 如果没有验证,跳过包含可忽略空白的TEXT 节点。
2. 提取节点的文本时:
a. 从CDATA 节点和文本节点提取文本。
b. 收集文本时忽略注释、属性和处理指令。
c. 如果遇到了实体引用节点或另一个元素节点,递归。(也就是说,将文本提取过程应用于所有的子节点)。
--------------------------------------------------------------------------------
注意:JAXP 1.2解析器不将实体引用节点插入DOM。相反,它插入包含引用内容的TEXT 节
上一个:变量声明并不是真正的语句
下一个:权限组件所缺的文件