分享
 
 
 

开启DOMAPI之门

王朝other·作者佚名  2008-05-21
窄屏简体版  字體: |||超大  

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 打下坚实的基础。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有