椭圆代表 XML 元素。方形代表数据,从name元素出发的直线代表 XML 属性。图中没有详细标出 address 元素。
在 Java 程序中存取 XML 数据的一些简单步骤
现在,我们用特定的 DTD JAVA 代码来说明 DOM API 的一个重要部分。特别的,我们给出以下DOM方法的用法。
getDocType getName getElementsByTagName item getFirstChild getNodeValue getAttribute getChildNodes getLength getTagName在本文的范例代码中,所有带下划线的方法调用都是 DOM API 的一部分。另有完整的 源代码可供下载。
定义存取 DOM 文档的一个简单接口
第一步是先定义一个抽象接口,该接口能够大大简化使用 XML 文档的代码。对于我们所使用的范例 DTD,定义以下接口:
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/webkaifa/)
interface order { String creditCard(); String billingName(); double totalPrice(); boolean authorizeCredit();}
存取 XML 数据的代码简单地调用由接口定义的操作集。
应当注意的是,这个接口的实现可有多种不同方式,其中的一些与 XML 无关(例如,某个实现可向数据库发出请求)。当然,这里我们只关心用DOM API 来实现处理 XML 数据的操作,这些数据同前面基于DTD的订单一致。
实现接口
现在编写一个实现该接口并封装 DOM 文档的类。例如,我们定义:
class orderImpl implements order { Document theDocument;
在构建器中将 DOM 文档捆绑至封装类
将 DOM 文档传递给封装类的构建器。构建器检查文档类型,以确定该文档确实符合订单 DTD。记住这些代码只是针对这个 DTD 的,并对数据的结构和内容做出假设。
public orderImpl(Document document) throws Exception { theDocument = document; DocumentType docType = theDocument.getDoctype(); if (docType==null) throw new Exception("Cannot determine document type."); if (!docType.getName().equals("order")) throw new ? Exception("Document is not an order.");}
注意,代码利用 getDoctype 操作来得到文档类型,该操作是在 Document 接口中由DOM定义的。getDoctype 操作返回一个支持 DocumentType 接口的对象。它的名字代表了文档的类型。
另请注意,一些 DOM 实现对 getDoctype 操作返回 null,这样就不能用于这个构建器。
给出用于将该文档捆绑至包装类的代码后,实现这个接口。在接口范例中给出的每项操作,说明了如何使用 DOM 完成特定任务。
返回特定元素的值
creditCard 方法说明了如何返回一个特定元素的值。它使用了在 Document 接口中定义的 getElementsByTagName 操作。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/webkaifa/)
public String creditCard() { NodeList nl = theDocument.getElementsByTagName("creditCard"); return nl.item(0).getFirstChild().getNodeValue();}
通常,getElementsByTagName 返回一个元素列表。因为在我们的样例 DTD 中只有一个名为creditCard的元素,所以这个列表中只包含一个元素,即 item(0)。这样,nl.item(0) 可用下图表示:
creditCard 元素的字符串值可通过在信用卡节点上调用 getFirstChild().getNodeValue() 方法得到。
注意,getElementsByTagName(elementName) 操作返回文档中利用 elementName 来命名的 所有元素。根据定义,它将前序遍历文档树来返回元素。
由于元素名称 creditCard 在我们的样例中是唯一的,所以可以直接找到该元素。然而,其他的元素(例如 name)不是唯一的。我们不能直接使用 getElementsByTagName 返回的第一个元素。实际上,billing 的一个子元素的名称为 name,另外 shipping 的一个子元素也叫做 name。
从唯一的子树中返回一个元素的属性值
在诸如 name 这样的名称不唯一的情况下,billingName 方法是获得元素值的一种方法。注意 name 在这个文档中不唯一,但是在 billing 子树中却是唯一的。另外还要注意,billing 元素在整个文档中是唯一的。这样,我们可以在文档中简单地调用 getElementsByTagName("billing"),然后在返回的 billing 元素中调用 getElementsByTagName。由于 getElementsByTagName 也是在 DOM API 中的 Element 接口中定义的,所以可以这样做。
public String billingName() { NodeList bl = theDocument.getElementsByTagName("billing"); NodeList nl = ((Element)bl.item(0)).getElementsByTagName("name"); Element name = (Element)nl.item(0); return name.getAttribute("given")+" "+name.getAttribute("family");}
利用 billingName 方法,还可以说明另一个技术,即获得一个元素的属性值。请注意,在我们的 DTD 中,name 元素被定义了两个属性: given 和 family 。getAttribute 操作由 Element 接口定义,它返回属性的文本值。
从多子树中返回元素的值
现在考虑 price 元素。我们不能再使用刚才的方法,因为 price 是文档的一个子元素,同时也是每个 item 元素的子元素。totalPrice 方法说明了另外一种查找非唯一元素值的方法。依据文档结构可知,我们需要的是顶层的 price 元素。
public double totalPrice() { NodeList nl=theDocument.getDocumentElement().getChildNodes(); Element candidateElement=null; for (int i=0; inl.getLength(); i++) { if (nl.item(i) instanceof Element) { candidateElement = (Element)nl.item(i); if (candidateElement.getTagName().equals("price")) break; } } return Double.parseDouble(candidateElement.getFirstChild().getNodeValue());}
getDocumentElement 操作返回一个描述文档的元素。从这里通过 getChildNodes 得到它的子节点。通过分析 DTD,可以看出子元素只有一个 header 元素,至少一个 item 元素和一个 price 元素。所以我们只要循环查找子节点,直到我们找到一个名为 price 的子元素。
同样,我们一旦得到这个元素,则调用 getFirstChild().getNodeValue()来获得它的值。
抽象
creditCard、billingName 及 totalPrice 在我们的接口中是 基本的 操作。它们简单地查找并返回相应的 XML 元素。另一方面,我们的接口也包含抽象的 authorizeCredit 方法。在 XML 文档中没有与它相应的元素。
下面给出 authorizeCredit 实现。它简单地使用我们已经在包装类中实现的 billingName、creditCard 及 totalPrice 方法。
public boolean authorizeCredit() { // illustrates abstraction return authorize( this.billingName(), this.creditCard(), this.totalPrice()); }
类的客户端使用
我们定义了一个抽象接口来访问我们的 XML 文档并通过使用 10 个重要的 DOM 操作来在包装类中实现了它们。接下来,我们介绍一下怎样通过Java代码来利用这些已经定义好的接口
下面的代码简单地调用语法分析器并将其返回的 DOM 文档传递给我们的包装类的构造器,然后调用我们实现的每一个方法。这些代码的编写使用了 IBM XML Parser for Java (参见 资源)。其他语法分析器的使用大致相同。
import com.ibm.xml.xpk4j.xml4j2.*;import java.io.*;public class test { public static void main(String[] args) { XML4J2DOMSource parser = new XML4J2DOMSource(); try { parser.parse("order.xml"); order theOrder = new orderImpl(parser.getDocument()); System.out.println("The credit card is "+theOrder.creditCard()); System.out.println("The total price is "+theOrder.totalPrice()); System.out.println("The billing name is "+theOrder.billingName()); theOrder.authorizeCredit() } catch(Exception e) { e.printStackTrace(); } }}
我们通过这个非常简单的例子向大家展示了 DOM API 的 10 个重要的操作。通过这些操作,我们说明了如何在已知 DTD 时进行查找、浏览、遍历元素以及获得元素及其属性值。这将为学习其它 DOM API 打下坚实的基础。