Java与XML联合编程之DOM篇.docx
《Java与XML联合编程之DOM篇.docx》由会员分享,可在线阅读,更多相关《Java与XML联合编程之DOM篇.docx(12页珍藏版)》请在冰豆网上搜索。
Java与XML联合编程之DOM篇
Java與XML聯合編程之DOM篇
來源︰網絡轉載作者︰揚帆時間︰2005-08-24點擊︰991
DOM初步
DOM是Document Object Model的縮寫,即文檔對象模型。
前面說過,XML將數據組織為一顆樹,所以DOM就是對這顆樹的一個對象描敘。
通俗的說,就是通過解析XML文檔,為XML文檔在邏輯上建立一個樹模型,樹的節點是一個個對象。
我們通過存取這些對象就能夠存取XML文檔的內容。
下面我們來看一個簡單的例子,看看在DOM中,我們是如何來操作一個XML文檔的。
這是一個XML文檔,也是我們要操作的對象︰
xml version="1.0" encoding="UTF-8"?
>
Good-bye serialization, hello Java!
下面,我們需要把這個文檔的內容解析到一個個的Java對象中去供程序使用,利用JAXP,我們只需幾行代碼就能做到這一點。
首先,我們需要建立一個解析器工廠,以利用這個工廠來獲得一個具體的解析器對象︰
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
我們在這里使用DocumentBuilderFacotry的目的是為了創建與具體解析器無關的程序,當DocumentBuilderFactory類的靜態方法newInstance()被調用時,它根據一個系統變量來決定具體使用哪一個解析器。
又因為所有的解析器都服從于JAXP所定義的接口,所以無論具體使用哪一個解析器,代碼都是一樣的。
所以當在不同的解析器之間進行切換時,只需要更改系統變量的值,而不用更改任何代碼。
這就是工廠所帶來的好處。
這個工廠模式的具體實現,可以參看下面的類圖。
DocumentBuilder db = dbf.newDocumentBuilder();
當獲得一個工廠對象後,使用它的靜態方法newDocumentBuilder()方法可以獲得一個DocumentBuilder對象,這個對象代表了具體的DOM解析器。
但具體是哪一種解析器,微軟的或者IBM的,對于程序而言並不重要。
然後,我們就可以利用這個解析器來對XML文檔進行解析了︰
Document doc = db.parse("c:
/xml/message.xml");
DocumentBuilder的parse()方法接受一個XML文檔名覂^ 淙氬問 禱匾桓ocument對象,這個Document對象就代表了一個XML文檔的樹模型。
以後所有的對XML文檔的操作,都與解析器無關,直接在這個Document對象上進行操作就可以了。
而具體對Document操作的方法,就是由DOM所定義的了。
Jaxp支持W3C所推薦的DOM 2。
如果你對DOM很熟悉,那麼下面的內容就很簡單了︰只需要按照DOM的規範來進行方法調用就可以。
當然,如果你對DOM不清楚,也不用著急,後面我們會有詳細的介紹。
在這兒,你所要知道並牢記的是︰DOM是用來描敘XML文檔中的數據的模型,引入DOM的全部原因就是為了用這個模型來操作XML文檔的中的數據。
DOM規範中定義有節點(即對象)、屬性和方法,我們通過這些節點的存取來存取XML的數據。
從上面得到的Document對象開始,我們就可以開始我們的DOM之旅了。
使用Document對象的getElementsByTagName()方法,我們可以得到一個NodeList對象,一個Node對象代表了一個XML文檔中的一個標簽元素,而NodeList對象,觀其名而知其意ㄐ@淼氖且桓ode對象的列表︰
NodeList nl = doc.getElementsByTagName("message");
我們通過這樣一條語句所得到的是XML文檔中所有標簽對應的Node對象的一個列表。
然後,我們可以使用NodeList對象的item()方法來得到列表中的每一個Node對象︰
Node my_node = nl.item(0);
當一個Node對象被建立之後,保存在XML文檔中的數據就被提取出來並封裝在這個Node中了。
在這個例子中,要提取Message標簽內的內容,我們通常會使用Node對象的getNodeValue()方法︰
String message = my_node.getFirstChild().getNodeValue();
請注意,這里還使用了一個getFirstChild()方法來獲得message下面的第一個子Node對象。
雖然在message標簽下面除了文本外並沒有其它子標簽或者屬性,但是我們堅持在這里使用getFirseChild()方法,這主要和W3C對DOM的定義有關。
W3C把標簽內的文本部分也定義成一個Node,所以先要得到代表文本的那個Node,我們才能夠使用getNodeValue()來獲取文本的內容。
現在,既然我們已經能夠從XML文件中提取出數據了,我們就可以把這些數據用在合適的地方,來構築應用程序。
下面的內容,我們將更多的關注DOM,為DOM作一個較為詳細的解析,使我們使用起來更為得心應手。
DOM詳解
1.基本的DOM對象
DOM的基本對象有5個︰Document,Node,NodeList,Element和Attr。
下面就這些對象的功能和實現的方法作一個大致的介紹。
Document對象代表了整個XML的文檔,所有其它的Node,都以一定的順序包含在Document對象之內,排列成一個樹形的結構,程序員可以通過遍歷這顆樹來得到XML文檔的所有的內容,這也是對XML文檔操作的起點。
我們總是先通過解析XML啈葆玫揭桓ocument對象,然後再來執行後續的操作。
此外,Document還包含了創建其它節點的方法,比如createAttribut()用來創建一個Attr對象。
它所包含的主要的方法有︰
createAttribute(String)︰用給定的屬性名創建一個Attr對象,並可在其後使用setAttributeNode方法來放置在某一個Element對象上面。
createElement(String)︰用給定的標簽名創建一個Element對象,代表XML文檔中的一個標簽,然後就可以在這個Element對象上添加屬性或進行其它的操作。
createTextNode(String)︰用給定的字符串創建一個Text對象,Text對象代表了標簽或者屬性中所包含的純文本字符串。
如果在一個標簽內沒有其它的標簽,那麼標簽內的文本所代表的Text對象是這個Element對象的唯一子對象。
getElementsByTagName(String)︰返回一個NodeList對象,它包含了所有給定標簽名字的標簽。
getDocumentElement()︰返回一個代表這個DOM樹的根節點的Element對象,也就是代表XML文檔根元素的那個對象。
Node對象是DOM結構中最為基本的對象,代表了文檔樹中的一個抽象的節點。
在實際使用的時候,很少會真正的用到Node這個對象,而是用到諸如Element、Attr、Text等Node對象的子對象來操作文檔。
Node對象為這些對象提供了一個抽象的、公共的根。
雖然在Node對象中定義了對其子節點進行存取的方法,但是有一些Node子對象,比如Text對象,它並不存在子節點,這一點是要注意的。
Node對象所包含的主要的方法有︰
appendChild(org.w3c.dom.Node)︰為這個節點添加一個子節點,並放在所有子節點的最後,如果這個子節點已經存在,則先把它刪掉再添加進去。
getFirstChild()︰如果節點存在子節點,則返回第一個子節點,對等的,還有getLastChild()方法返回最後一個子節點。
getNextSibling()︰返回在DOM樹中這個節點的下一個兄弟節點,對等的,還有getPreviousSibling()方法返回其前一個兄弟節點。
getNodeName()︰根據節點的類型返回節點的名稱。
getNodeType()︰返回節點的類型。
getNodeValue()︰返回節點的值。
hasChildNodes()︰判斷是不是存在有子節點。
hasAttributes()︰判斷這個節點是否存在有屬性。
getOwnerDocument()︰返回節點所處的Document對象。
insertBefore(org.w3c.dom.Node new,org.w3c.dom.Node ref)︰在給定的一個子對象前再插入一個子對象。
removeChild(org.w3c.dom.Node)︰刪除給定的子節點對象。
replaceChild(org.w3c.dom.Node new,org.w3c.dom.Node old)︰用一個新的Node對象代替給定的子節點對象。
NodeList對象,顧名思義,就是代表了一個包含了一個或者多個Node的列表。
可以簡單的把它看成一個Node的數組,我們可以通過方法來獲得列表中的元素︰
GetLength()︰返回列表的長度。
Item(int)︰返回指定位置的Node對象。
Element對象代表的是XML文檔中的標簽元素,繼承于Node,亦是Node的最主要的子對象。
在標簽中可以包含有屬性,因而Element對象中有存取其屬性的方法,而任何Node中定義的方法,也可以用在Element對象上面。
getElementsByTagName(String)︰返回一個NodeList對象,它包含了在這個標簽中其下的子孫節點中具有給定標簽名字的標簽。
getTagName()︰返回一個代表這個標簽名字的字符串。
getAttribute(String)︰返回標簽中給定屬性名稱的屬性的值。
在這兒需要主要的是,應為XML文檔中允許有實體屬性出現,而這個方法對這些實體屬性並不適用。
這時候需要用到getAttributeNodes()方法來得到一個Attr對象來進行進一步的操作。
getAttributeNode(String)︰返回一個代表給定屬性名稱的Attr對象。
Attr對象代表了某個標簽中的屬性。
Attr繼承于Node,但是因為Attr實際上是包含在Element中的,它並不能被看作是Element的子對象,因而在DOM中Attr並不是DOM樹的一部分,所以Node中的getparentNode(),getpreviousSibling()和getnextSibling()返回的都將是null。
也就是說,Attr其實是被看作包含它的Element對象的一部分,它並不作為DOM樹中單獨的一個節點出現。
這一點在使用的時候要同其它的Node子對象相區別。
需要說明的是,上面所說的DOM對象在DOM中都是用接口定義的,在定義的時候使用的是與具體語言無關的IDL語言來閎?
W摹R蚨OM其實可以在任何面向對象的語言中實現,只要它實現了DOM所定義的接口和功能就可以了。
同時,有些方法在DOM中並沒有定義,是用IDL的屬性來表達的,當被映射到具體的語言時,這些屬性被映射為相應的方法。
2.DOM實例
有了上面的介紹,相信你對DOM理解的更多了吧。
下面的例子將讓你對DOM更加熟悉起來。
先說說這個例子到底要做的是什麼吧,我們希望在一個名為link.xml文件中保存了一些URL地址,通過一個簡單的程序,我們可以通過DOM把這些URL讀出並顯示出來,也可以反過來向這個XML文件中寫入加入的URL地址。
很簡單,卻很實用,也足夠來例示DOM的絕大部分用法了。
XML文件本身不復雜,就不給出它的DTD了。
link.xml:
xml version="1.0" standalone="yes"?
>
JSP Insider
JSP Insider
2
1
2001
A JSP information site.
The makers of Java
Sun Microsystems
3
1
2001
Sun Microsystem's website.
The standard JSP container
http:
//jakarta.apache.org
Apache Group
4
1
2001
Some great software.
第一個程序我們稱為xmldisplay.java,具體的程序清單可以在附件中找到。
主要的功能就是讀取這個XML文件中各個節點的內容,然後在格式化輸出在System.out上,我們來看看這個程序︰
import javax.xml.parsers.*;
import org.w3c.dom.*;
這是引入必要的類,因為在這里使用的是Sun所提供的XML解析器,因而需要引入java.xml.parsers包,其中包含了有DOM解析器和SAX解析器的具體實現。
org.w3c.dom包中定義了w3c所制定的DOM接口。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("links.xml");
doc.normalize();
除了上面講到的,還有一個小技巧,對Document對象調用normalize(),可以去掉XML文檔中作為格式化內容的空白而映射在DOM樹中的不必要的Text Node對象。
否則你得到的DOM樹可能並不如你所想象的那樣。
特別是在輸出的時候,這個normalize()更為有用。
NodeList links =doc.getElementsByTagName("link");
剛才說過,XML文檔中的空白符也會被作為對象映射在DOM樹中。
因而,直接調用Node方法的getChildNodes方法有時候會有些問題,有時不能夠返回所期望的NodeList對象。
解決的辦法是使用Element的getElementByTagName(String),返回的NodeLise就是所期待的對象了。
然後,可以用item()方法提取想要的元素。
for (int i=0;iElement link=(Element) links.item(i);
System.out.print("Content:
");
System.out.println(link.getElementsByTagName("text").item(0).getFirstChild().getNodeValue());
System.out.print("URL:
");
System.out.println(link.getElementsByTagName("url").item(0).getFirstChild().getNodeValue());
System.out.print("Author:
");
System.out.println(link.getElementsByTagName("author").item(0).getFirstChild().getNodeValue());
System.out.print("Date:
");
Element linkdate=(Element) link.getElementsByTagName("date").item(0);
String day=linkdate.getElementsByTagName("day").item(0).getFirstChild().getNodeValue();
String month=linkdate.getElementsByTagName("month").item(0).getFirstChild().getNodeValue();
String year=linkdate.getElementsByTagName("year").item(0).getFirstChild().getNodeValue();
System.out.println(day+"-"+month+"-"+year);
System.out.print("Description:
");
System.out.println(link.getElementsByTagName("description").item(0).getFirstChild().getNodeValue());
System.out.println();
}
上面的代碼片斷就完成了對XML文檔內容的格式化輸出。
只要注意到一些細節的問題,比如getFirstChile()方法和getElementsByTagName()方法的使用,這些還是比較容易的。
下面的內容,就是在修改了DOM樹後重新寫入到XML文檔中去的問題了。
這個程序名為xmlwrite.java。
在JAXP1.0版本中,並沒有直接的類和方法能夠處理XML文檔的寫入問題,需要借助其它包中的一些輔助類。
而在JAXP1.1版本中,引入了對XSLT的支持,所謂XSLT,就是對XML文檔進行變換(Translation)後,得到一個新的文檔結構。
利用這個新加入的功能,我們就能夠很方便的把新生成或者修改後的DOM樹從新寫回到XML文件中去了,下面我們來看看代碼的實現,這段代碼翔x饕 δ蓯竅inks.xml文件中加入一個新的link節點︰
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
新引入的java.xml.transform包中的幾個類,就是用來處理XSLT變換的。
我們希望在上面的XML文件中加入一個新的link節點,因而首先還是要讀入links.xml文件,構建一個DOM樹,然後再對這個DOM樹進行修改(添加節點),最後把修改後的DOM寫回到links.xml文件中︰
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("links.xml");
doc.normalize();
//---取得變量----
String text="Hanzhong's Homepage";
String url="";
String author="Hzliu Liu";
String discription="A site from Hanzhong Liu, give u lots of suprise!
!
!
";
為了看清重點,簡化程序,我們把要加入的內容硬編碼到記憶String對象中,而實際操作中,往往利用一個界面來提取用戶輸入,或者通過JDBC從數據庫中提取想要的內容。
Text textseg;
Element link=doc.createElement("link");
首先應該明了的是,無論什麼類型的Node,Text型的也好,Attr型的也好,Element型的也好,它們的創建都是通過Document對象中的createXXX()方法來創建的(XXX代表具體要創建的類型),因此,我們要向XML文檔中添加一個link項目,首先要創建一個link對象︰
Element linktext=doc.createElement("text");
textseg=doc.createTextNode(text);
linktext.appendChild(textseg);
link.appendChild(linktext);
Element linkurl=doc.createElement("url");
textseg=doc.createTextNode(url);
linkurl.appendChild(textseg);
link.appendChild(linkurl);
Element linkauthor=doc.createElement("author");
textseg=doc.createTextNode(author);
linkauthor.appendChild(textseg);
link.appendChild(linkauthor);
java.util.Calendar rightNow = java.util.Calendar.getInstance();
String day=Integer.toString(rightNow.get(java.util.Calendar.DAY_OF_MONTH));
String month=Integer.toString(rightNow.get(java.util.Calendar.MONTH));
String year=Integer.toString(rightNow.get(java.util.Calendar.YEAR));
Element linkdate=doc.createElement("dat