文档

Java™教程
隐藏目录
何时使用DOM
路径: Java XML处理API (JAXP)
课程: 文档对象模型(DOM)

何时使用DOM

文档对象模型(DOM)标准首先是为文档(例如文章和书籍)设计的。此外,JAXP 1.4.2实现还支持XML Schema,这对于任何应用程序都可能是一个重要的考虑因素。

另一方面,如果你处理的是简单的数据结构,并且XML Schema在你的计划中不是很重要的一部分,那么你可能会发现更适合你目的的是更多面向对象的标准之一,例如JDOM或dom4j。

从一开始,DOM就被设计为语言中立。由于它是为像C和Perl这样的语言设计的,DOM不利用Java的面向对象特性。这个事实,加上文档和数据之间的区别,也有助于解释DOM处理与JDOM或dom4j结构处理不同的方式。

在本节中,我们将检查这些标准底层模型之间的差异,以帮助您选择最适合您应用程序的模型。

文档与数据

DOM中使用的文档模型与JDOM或dom4j中使用的数据模型之间的主要区别在于:

在编程这两个模型时,节点在数据层次结构中的构成方式的不同是造成差异的主要原因。然而,对混合内容的支持能力,比任何其他因素都更能解释标准定义节点的方式的差异。因此,我们首先来研究DOM的混合内容模型。

混合内容模型

在DOM层次结构中,文本和元素可以自由混合。这种结构在DOM模型中被称为混合内容。

混合内容在文档中经常出现。例如,假设你想要表示这个结构:

<sentence>这是一个<bold>重要</bold>的想法。</sentence>

DOM节点的层次结构看起来可能是这样的,每一行代表一个节点:

ELEMENT: sentence
   + TEXT: 这是一个
   + ELEMENT: bold
     + TEXT: 重要
   + TEXT: 的想法.

请注意,sentence元素包含文本,然后是一个子元素,然后是其他文本。混合文本和元素定义了混合内容模型。

节点类型

为了提供混合内容的能力,DOM节点本质上非常简单。在上面的例子中,第一个元素的“内容”(其值)仅仅标识了它是什么类型的节点。

首次使用DOM的用户通常会对这个事实感到困惑。在导航到<sentence>节点之后,他们会询问节点的“内容”,并期望得到一些有用的东西。相反,他们只能找到元素的名称sentence


注意 - DOM节点API定义了nodeValue()nodeType()nodeName()方法。对于第一个元素节点,nodeName()返回sentence,而nodeValue()返回null。对于第一个文本节点,nodeName()返回#textnodeValue()返回“This is an ”。重要的是,元素的与其内容不同。


在上面的示例中,询问句子的“文本”是什么意思?根据您的应用程序,以下任何一种都可以合理:

一个更简单的模型

使用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 are using
        + TEXT: Eagle
       + CDATA: <i>project</i>
       + TEXT: is
       + PI: editor: red
       + ELEMENT: bold
          + TEXT: important
       + PI: editor: normal

这个示例描述了DOM中可能出现的节点类型。虽然您的应用程序大部分时间可能会忽略它们,但一个真正强大的实现需要识别并处理每个节点。

同样,导航到节点的过程涉及处理子元素,忽略您不感兴趣的元素并检查您感兴趣的元素,直到找到您感兴趣的节点。

一个在固定、内部生成的数据上工作的程序可以承担简化的假设:处理指令、注释、CDATA节点和实体引用在数据结构中不存在。但是,对来自外部世界的各种数据(特别是数据)进行处理的真正强大的应用程序必须准备处理所有可能的XML实体。

(一个“简单”的应用程序只能在输入数据包含它所期望的简化的XML结构时工作。但没有验证机制来确保更复杂的结构不存在。毕竟,XML的设计目的就是允许它们存在。)

为了更加强大,DOM应用程序必须执行以下操作:

  1. 在搜索元素时:
    1. 忽略注释、属性和处理指令。
    2. 允许子元素的顺序不按预期出现的可能性。
    3. 如果不进行验证,跳过包含可忽略空格的TEXT节点。
  2. 在提取节点的文本时:
    1. 从CDATA节点和文本节点中提取文本。
    2. 在收集文本时,忽略注释、属性和处理指令。
    3. 如果遇到实体引用节点或其他元素节点,进行递归(即,将文本提取过程应用于所有子节点)。

当然,许多应用程序不必担心这些问题,因为它们所见到的数据将受到严格控制。但是,如果数据可以来自各种外部来源,那么应用程序可能需要考虑这些可能性。

您需要执行这些功能的代码在本课程的末尾给出,搜索节点获取节点内容。目前,我们只需要确定DOM是否适合您的应用程序。

选择您的模型

正如您所看到的,当您使用DOM时,即使是从节点获取文本这样简单的操作也需要一些编程。因此,如果您的程序处理简单的数据结构,那么JDOM、dom4j甚至1.4正则表达式包(java.util.regex)可能更适合您的需求。

然而,对于完整的文档和复杂的应用程序,DOM为您提供了很大的灵活性。如果您需要使用XML Schema,那么DOM仍然是最佳选择 - 至少目前是这样。

如果您在开发的应用程序中同时处理文档和数据,那么DOM可能仍然是您最好的选择。毕竟,一旦您编写了用于检查和处理DOM结构的代码,就很容易为特定目的进行定制。因此,选择在DOM中执行所有操作意味着您只需要处理一组API,而不是两组。

此外,DOM标准是一种内存中文档模型的标准化标准。它功能强大且稳定,并且有许多实现。这对于许多大型安装来说是一个重要的决策因素,特别是对于需要最小化由于API更改而产生的成本的大规模应用程序。

最后,即使通讯录中的文本今天不允许加粗、斜体、颜色和字体大小,将来您可能会想要处理这些内容。由于DOM可以处理几乎任何您提供的内容,选择DOM可以更轻松地为您的应用程序提供未来的支持。


上一页: 文档对象模型
下一页: 将XML数据读入DOM