用Java访问XML数据的简单起步
2008-01-05 08:35:34 来源:WEB开发网核心提示:xml 是现在非常流行的数据表达格式,其特点是可移植、与平台无关以及具有直接可读的形式,用Java访问XML数据的简单起步,Document Object Model (DOM) 是应用程序存取 XML 数据的接口,不幸的是,以创建一个用来处理 XML 并且能够很轻易地融入分布式应用的 Java Beans 的知识库,
xml 是现在非常流行的数据表达格式,其特点是可移植、与平台无关以及具有直接可读的形式。Document Object Model (DOM) 是应用程序存取 XML 数据的接口。不幸的是,DOM 是一种相当复杂的 API,因而较难以迅速把握。但是,如能知道所存取数据的 DTD,这时就轻易得多了。本文将通过若干简单步骤,对如何利用 java 版的 DOM 来存取 XML 数据进行介绍。
可扩展标记语言 (XML) 已经相当普及,它是一种可移植的、与平台无关的且直接可读的数据格式。许多软件厂商均已宣称“支持 XML”,这通常是指他们的软件产品将生成或用到 XML 格式的数据。
XML 也同样被看作是企业间交流数据的通用格式。它答应企业在 XML 文档类型定义(即 DTD)的基础上对所交流的数据取得一致。这些 DTD 文件独立于企业中所使用的数据类型。
许多标准化组织正在致力于规范交流数据的 DTD。其中一个例子就是国际出版通信委员会(请参见资源)已经定义了一个 XML 的 DTD,这个 DTD 可以使“所传输的带有标记的新闻信息能够轻松地转换为电子出版格式”。这些市场标准将使不同应用程序之间能够在未事先确定方式的情况下进行数据的交换。
由 W3C 定义的 XML 规范(请参见资源)中规定了 XML 的语法和语义。一个 XML 文档必须经过语法分析才能被处理。假如每个程序都必须先对 XML 进行语法分析再去处理,那将是非常困难的,因为给出这种语言的语法和语义是很复杂的。W3C 已经定义了文档对象模型(DOM)(请参见资源)来解决这一问题。DOM 是一个针对XML数据的应用编程接口。大部分 XML 语法分析器都为所分析的 XML 生成一个 DOM 描述。
DOM 标准
DOM API 被定义为一系列 CORBA IDL 接口(请参见资源)。它用一个抽象树来描述一个经过语法分析的 XML 文档。之所以说它是抽象的,这是因为只有这些接口反映出树形的结构。而用来实现抽象树的实际数据结构和算法不必是树形结构。
由于 DOM API 是以 CORBA IDL 形式规定的,所以它被许多编程语言所支持,包括 Java 语言。我们假定本文中使用标准的 Java 语言。DOM 规范给出了具体的基于Java 接口。
DOM 第一层规范是在 1998 年被采用的。它留下一些保留部分,以根据后来的实践经验来进一步扩充。DOM 第二层规范在第一层的基础上增加了对 XML 命名空间、文档创建、视图和式样单等内容的支持。第二层规范尚有待公众评价。虽然从技术上而言还没有最终完成,但是也已经相当稳定。
对于一个 XML 文档,许多 XML 语法分析器均可供 Java 程序使用,以生成 DOM 的第一层描述。因此,这里的代码只假定为基于DOM 的第一层子集。
通用或特定 DTD 代码
在Java中使用DOM API编写的代码要么是通用的,要么是基于特定的DTD。通用代码在所有 XML 文档中都能正常工作。通用代码一般更难编写,因为它通常必须遍历整个 DOM 树,考虑各种可能。代码不能依靠任何特定的元素、属性和文档结构。通用代码用于处理一般性事务,例如检查文档中的拼写错误、计算文字数目、通过网络发送文件,等等。
另一方面,特定 DTD 代码是在根据特定的 DTD 写出的。它不能用来操作由另一种 DTD 定义的 XML 文档。特定的 DTD 比较轻易编写,因为它假设 XML 文档具有该特定 DTD 所指定的格式。例如,假设一个 DTD 声明一个名为“NAME”的元素要求具有一个名为“GIVEN”的属性,Java 代码可以假设这个属性存在并通过简单地 DOM getAttribute() 调用来访问它
这篇文章将帮助你编写特定 DTD Java 代码。在此之后的任务则是学习如何编写通用 DTD 代码。
一个示例
为了解释怎样在 Java 程序中使用 DOM API,我们将用一个定购程序作为例子,因为它是一个典型的基于 XML 的 B2B 应用,并且具有很丰富的 XML 结构。
下面是我们所用定购单的 DTD。
<?xml encoding="US-ASCII"?>
<!ELEMENT order (header,item+,PRice)>
<!ELEMENT header (billing,shipping)>
<!ELEMENT billing (name,address,creditCard)>
<!ELEMENT shipping (name,address)>
<!ELEMENT name EMPTY>
<!ATTLIST name
given CDATA #REQUIRED
family CDATA #REQUIRED
>
<!ELEMENT address (street,city,state,zipcode,country,phone)>
<!ELEMENT item (prodId,prodName,quantity,price)>
<!ELEMENT creditCard (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zipcode (#PCDATA)>
<!ELEMENT country (#PCDATA)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT prodId (#PCDATA)>
<!ELEMENT prodName (#PCDATA)>
<!ELEMENT quantity (#PCDATA)>
<!ELEMENT price (#PCDATA)>
下面是 包含100 个装饰品的定购单的 XML 文档:
<?xml version="1.0"?>
<!DOCTYPE order SYSTEM "order.dtd">
<order>
<header>
<billing>
<name given="Jane" family="Doe"/>
<address>
<street>555 Main Street</street>
<city>Mill Valley</city>
<state>California</state>
<zipcode>94520</zipcode>
<country>USA</country>
<phone>707 555-1000</phone>
</address>
<creditCard>4555 5555 5555 5555</creditCard>
</billing>
<shipping>
<name given="John" family="Doe"/>
<address>
<street>100 Main Street</street>
<city>Brisbane</city>
<state>California</state>
<zipcode>94005</zipcode>
<country>USA</country>
<phone>415 555-9999</phone>
</address>
</shipping>
</header>
<item>
<prodId>5555555</prodId>
<prodName>Widget</prodName>
<quantity>100</quantity>
<price>.25</price>
</item>
<price>25.00</price>
</order>
XML 语法分析器将上面的 XML 文档抽象地描述成一个树。这个树形结构可以用图形描述如下:
椭圆代表 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,定义以下接口:
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 操作。
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; i<nl.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 打下坚实的基础。
资源
XML 语法分析器及 DOM 实现
假如您想用 DOM API 编写 Java 代码,就需要一个 DOM 的实现,也就是一个语法分析器。幸运的是,现在已经可以找到一些了。这里有一些相关的资源:
Apache Xerces XML Parser
IBM's XML Parser for Java
Oracle (要访问 Oracle Technology Network,您必须成为其会员。注册是免费的。)
Sun's Java Project X
Xbeans
Xbeans 是一个开放源代码的 Java Beans 知识库,使用最一般的方法来处理 DOM 文档。通过构造 Xbeans,可以创建分布式应用程序来处理 XML。这里是通用DOM 代码的极佳来源。
jGuru XML FAQ
国际出版通讯委员会 (International Press Telecommunications Council)是一个对数据交流中的 DTD 进行统一的标准化组织。
XML 规范
DOM 规范
CORBA IDL
关于作者
BrUCe Martin 是分布式对象计算领域的一位带头人。90 年代初期,他在惠普实验室设计并实现了一种接口定义语言,它后来成为惠普最初的 CORBA 子项目的基础。在 Sun 微系统公司,他是 Sun 的 CORBA 体系结构设计者之一,同时还是五项 OMG 的 CORBA 服务规范的制定者。在 Inprise 公司,Bruce 是该公司第一个以 CORBA 为基础的 Java 应用服务器的设计者和开发者。Bruce 具有关于 Java、XML 和 DOM 的渊博的实践经验。
现在,Bruce 已经成为 jGuru 的软件领袖。他正在致力于维护 xbeans.org(一个开放源代码计划),以创建一个用来处理 XML 并且能够很轻易地融入分布式应用的 Java Beans 的知识库。
Bruce 在加利福尼亚大学圣地亚哥分校获得了计算机科学硕士和博士学位,此前他在位于加利福尼亚大学伯克利分校获得了计算机科学学士学位。
http://www-900.ibm.com/developerWorks/cn/xml/jguru-dom/index.sHtml
更多精彩
赞助商链接