DOM 是 Document Object Model 的縮寫,即文檔對象模型。 XML 將數據組織為一顆樹,所以 DOM 就是對這顆樹的一個對象描敘。通俗的說,就是通過解析 XML 文檔,為 XML 文檔在邏輯上建立一個樹模型,樹的節點是一個個對象。我們通過存取這些對象就能夠存取 XML 文檔的內容。
下面我們來看一個簡單的例子,看看在 DOM 中,我們是如何來操作一個 XML 文檔的。
?
這是一個 XML 文檔,也是我們要操作的對象:
?
<?xml version="1.0" encoding="UTF-8"?>
<messages>
<message>Good-bye serialization, hello Java!</message>
</messages>
?
下面,我們需要把這個文檔的內容解析到一個 Java 對象中去供程序使用,利用 JAXP ,我們只需幾行代碼就能做到這一點。首先,我們需要建立一個解析器工廠,以利用這個工廠來獲得一個具體的解析器對象:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
我們在這里使用 DocumentBuilderFacotry 的目的是為了創建與具體解析器無關的程序,當 DocumentBuilderFactory 類的靜態方法 newInstance() 被調用時,它根據一個系統變量來決定具體使用哪一個解析器。又因為所有的解析器都服從于 JAXP 所定義的接口,所以無論具體使用哪一個解析器,代碼都是一樣的。所以當在不同的解析器之間進行切換時,只需要更改系統變量的值,而不用更改任何代碼。這就是工廠所帶來的好處。這個工廠模式的具體實現,可以參看下面的類圖。
??
DocumentBuilder builder = factory.newDocumentBuilder();
當獲得一個工廠對象后,使用它的靜態方法 newDocumentBuilder() 方法可以獲得一個 DocumentBuilder 對象,這個對象代表了具體的 DOM 解析器。但具體是哪一種解析器,微軟的或者 IBM 的,對于程序而言并不重要。
?
API javax.xml.parsers.DocumentBuilderFactory 1.4
返回 DocumentBuilderFactory 類的一個實例
static DocumentBuilderFactory newInstance()
返回 DocumentBuilder 類的一個實例
DocumentBuilder newDocumentBuilder()
?
然后,我們就可以利用這個解析器來對 XML 文檔進行解析了:
Document doc = builder.parse("src/message.xml");
?
DocumentBuilder 的 parse() 方法接受一個 XML 文檔名作為輸入參數,返回一個 Document 對象,這個 Document 對象就代表了一個 XML 文檔的樹模型。以后所有的對 XML 文檔的操作,都與解析器無關,直接在這個 Document 對象上進行操作就可以了。而具體對 Document 操作的方法,就是由 DOM 所定義的了。
?
API javax.xml.parsers.DocumentBuilder 1.4
解析來自給定文件、 URL 或輸入流的 XML 文檔,返回解析后的文檔
-
Document parse(File f)
-
Document parse(String url)
-
Document parse(InputStream in)
JAXP 支持 W3C 所推薦的 DOM 2 。如果你對 DOM 很熟悉,只需要按照 DOM 的規范來進行方法調用就可以。 DOM 是用來描敘 XML 文檔中的數據的模型,引入 DOM 的全部原因就是為了用這個模型來操作 XML 文檔的中的數據。 DOM 規范中定義有節點(即對象)、屬性和方法,我們通過這些節點的存取來存取 XML 的數據。
上面得到了 Document 對象,使用它的 getElementsByTagName() 方法,我們可以得到一個 NodeList 對象,一個 Node 對象代表了一個 XML 文檔中的一個標簽元素,而 NodeList 對象,觀其名而知其意,所代表的是一個 Node 對象的列表:
NodeList children = doc.getElementsByTagName("message");
我們通過這樣一條語句所得到的是 XML 文檔中所有 <message> 標簽對應的 Node 對象的一個列表。然后,我們可以使用 NodeList 對象的 item() 方法來得到列表中的每一個 Node 對象:
Node child = children.item(0);
當一個 Node 對象被建立之后,保存在 XML 文檔中的數據就被提取出來并封裝在這個 Node 中了。在這個例子中,要提取 Message 標簽內的內容,我們通常會使用 Node 對象的 getNodeValue() 方法: String message = child.getFirstChild().getNodeValue();
注意: 這里還使用了一個 getFirstChild() 方法來獲得 message 下面的第一個子 Node 對象。雖然在 message 標簽下面除了文本外并沒有其它子標簽或者屬性,但是我們堅持在這里使用 getFirseChild() 方法,這主要和 W3C 對 DOM 的定義有關。 W3C 把標簽內的文本部分也定義成一個 Node ,所以先要得到代表文本的那個 Node ,我們才能夠使用 getNodeValue() 來獲取文本的內容。
現在,既然已經能夠從 XML 文件中提取出數據了,就可以把這些數據用在合適的地方,來構筑應用程序。
DOM 的基本對象有 5 個: Document , Node , NodeList , Element 和 Attr 。如下結構圖。
?
Document 對象代表了整個 XML 的文檔,所有其它的 Node ,都以一定的順序包含在 Document 對象之內,排列成一個樹形的結構,程序員可以通過遍歷這顆樹來得到 XML 文檔的所有的內容,這也是對 XML 文檔操作的起點。我們總是先通過解析 XML 源文件而得到一個 Document 對象,然后再來執行后續的操作。此外, Document 還包含了創建其它節點的方法,比如 createAttribut() 用來創建一個 Attr 對象。它所包含的主要的方法有:
API org.w3c.dom.Document
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 對象所包含的主要的方法有:
API org.w3c.dom.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 的數組,我們可以通過方法來獲得列表中的元素:
?
API org.w3c.dom. NodeList
GetLength() : 返回列表的長度。
Item(int) : 返回指定位置的 Node 對象。
?
Element 對象代表的是 XML 文檔中的標簽元素,繼承于 Node ,亦是 Node 的最主要的子對象。在標簽中可以包含有屬性,因而 Element 對象中有存取其屬性的方法,而任何 Node 中定義的方法,也可以用在 Element 對象上面。
API org.w3c.dom. 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 語言來定義的。因而, DOM 其實可以在任何面向對象的語言中實現,只要它實現了 DOM 所定義的接口和功能就可以了。同時,有些方法在 DOM 中并沒有定義,是用 IDL 的屬性來表達的,當被映射到具體的語言時,這些屬性被映射為相應的方法。
<?xml version="1.0" encoding="UTF-8"?> <links> <link> <text>The makers of Java</text> <url newWindow="no">http://java.sun.com</url> <author>Sun Microsystems</author> <date> <day>5</day> <month>4</month> <year>2008</year> </date> <description>Sun Microsystem's website.</description> </link> <link> <text>Janwer's Homepage</text> <url>www.janwer.com</url> <author>張峻偉</author> <date> <day>5</day> <month>3</month> <year>2008</year> </date> <description> A site from Janwer Zhang, give u lots of suprise!!! </description> </link> <link> <text>張峻偉的個人主頁</text> <url>janwer.iteye.com</url> <author>張峻偉</author> <date> <day>5</day> <month>3</month> <year>2008</year> </date> <description>有關J2EE</description> </link> <link> <text>zhang janwer's Homepage</text> <url>www.junwei.com</url> <author>janwer zhang</author> <date> <day>6</day> <month>3</month> <year>2008</year> </date> <description>A site from J2EE,C#,C++,C and so on!</description> </link> </links>
?
我們希望在一個名為 server.xml 文件中保存了一些 URL 地址,通過一個簡單的程序,我們可以通過 DOM 把這些 URL 讀出并顯示出來,也可以反過來向這個 XML 文件中寫入加入的 URL 地址。
package cn.janwer.xml; import javax.xml.parsers.*; import org.w3c.dom.*; public class XmlRead { public static void main(String args[]) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("src/server.xml"); doc.normalize(); //以去掉XML文檔中作為格式化內容的空白 /** * XML文檔中的空白符也會被作為對象映射在DOM樹中。 * 因而,直接調用Node方法的 getChildNodes方法有時候會有些問題, * 有時不能夠返回所期望的NodeList對象。 * 解決的辦法是使用Element的 getElementByTagName(String), * 返回的NodeLise就是所期待的對象了。然后,可以用item()方法提取想要的元素。 */ NodeList links = doc.getElementsByTagName("link"); for (int i = 0; i < links.getLength(); i++) { Element 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().trim()); System.out.println(); } } catch (Exception e) { e.printStackTrace(); } } }
?
下面的內容,就是在修改了 DOM 樹后重新寫入到 XML 文檔中去的問題了。這個程序名為 Xmlwrite.java 。在 JAXP1.0 版本中,并沒有直接的類和方法能夠處理 XML 文檔的寫入問題,需要借助其它包中的一些輔助類。而在 JAXP1.1 版本中,引入了對 XSLT 的支持,所謂 XSLT ,就是對 XML 文檔進行變換( Translation )后,得到一個新的文檔結構。利用這個新加入的功能,我們就能夠很方便的把新生成或者修改后的 DOM 樹從新寫回到 XML 文件中去了,下面我們來看看代碼的實現,這段代碼的主要功能是向 links.xml 文件中加入一個新的 link 節點。
?
我們希望在上面的 XML 文件中加入一個新的 link 節點,因而首先還是要讀入 links.xml 文件,構建一個 DOM 樹,然后再對這個 DOM 樹進行修改(添加節點),最后把修改后的 DOM 寫回到 links.xml 文件中:
DocumentBuilderFactory factory = DocumentBuilderFactory. newInstance ();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("src/server.xml");
doc.normalize();
// --- 取得變量 ----
String text = "zhang janwer's Homepage";
String url = "www.junwei.com";
String author = "janwer zhang";
String discription = "A site from J2EE,C#,C++,C and so on!";
為了看清重點,簡化程序,我們把要加入的內容硬編碼到記憶 String 對象中,而實際操作中,往往利用一個界面來提取用戶輸入,或者通過 JDBC 從數據庫中提取想要的內容。
Text textseg;
Element link=doc.createElement("link");
?
首先應該明了的是,無論什么類型的 Node , Text 型的也好, Attr 型的也好, Element 型的也好,它們的創建都是通過 Document 對象中的 createXXX() 方法來創建的( XXX 代表具體要創建的類型)。
創建節點的過程可能有些千篇一律,但需要注意的地方是,對 Element 中所包含的 text (在 DOM 中,這些 text 也是代表了一個 Node 的,因此也必須為它們創建相應的 node ),不能直接用 Element 對象的 setNodeValue() 方法來設置這些 text 的內容,而需要用創建的 Text 對象的 setNodeValue() 方法來設置文本,這樣才能夠把創建的 Element 和其文本內容添加到 DOM 樹中。看看前面的代碼,你會更好的理解這一點:
doc.getDocumentElement().appendChild(link);
最后,不要忘記把創建好的節點添加到 DOM 樹中。 Document 類的 getDocumentElement() 方法,返回代表文檔根節點的 Element 對象。在 XML 文檔中,根節點一定是唯一的。
TransformerFactory tFactory =TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new java.io.File("links.xml"));
transformer.transform(source, result);
?
然后就是用 XSLT 把 DOM 樹輸出了。這里的 TransformerFactory 也同樣應用了工廠模式,使得具體的代碼同具體的變換器無關。實現的方法和 DocumentBuilderFactory 相同,這兒就不贅述了。 Transformer 類的 transfrom 方法接受兩個參數、一個數據源 Source 和一個輸出目標 Result 。這里分別使用的是 DOMSource 和 StreamResult ,這樣就能夠把 DOM 的內容輸出到一個輸出流中,當這個輸出流是一個文件的時候, DOM 的內容就被寫入到文件中去了。
package cn.janwer.xml; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.*; public class Xmlwriter { public static void main(String args[]) { try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("src/server.xml"); doc.normalize(); // ---取得變量---- String text = "zhang janwer's Homepage"; String url = "www.junwei.com"; String author = "janwer zhang"; String discription = "A site from J2EE,C#,C++,C and so on!"; // ------------- Text textseg; Element link = doc.createElement("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("date"); Element linkdateday = doc.createElement("day"); textseg = doc.createTextNode(day); linkdateday.appendChild(textseg); Element linkdatemonth = doc.createElement("month"); textseg = doc.createTextNode(month); linkdatemonth.appendChild(textseg); Element linkdateyear = doc.createElement("year"); textseg = doc.createTextNode(year); linkdateyear.appendChild(textseg); linkdate.appendChild(linkdateday); linkdate.appendChild(linkdatemonth); linkdate.appendChild(linkdateyear); link.appendChild(linkdate); Element linkdiscription = doc.createElement("description"); textseg = doc.createTextNode(discription); linkdiscription.appendChild(textseg); link.appendChild(linkdiscription); doc.getDocumentElement().appendChild(link); TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult( new java.io.File("src/server.xml")); transformer.transform(source, result); } catch (Exception e) { e.printStackTrace(); } } }
?
編輯 XML 文檔總結:
?? 1. 更改節點數據
???? Node.setNodeValue(elemValue);
?
?? 2. 添加節點
???? String totalString = new Double(total).toString();
????? ??? Node totalNode = doc.createTextNode(totalString); ?
????? ??? //Document 對象創建新的文本節點,該節點帶有作為值的 totalString
????? ??? Element totalElement = doc.createElement("total");
????? ??? // 創建新元素 total
????? ??? totalElement.appendChild(totalNode); ????????????????????????
????? ??? // 將節點添加到新的 total 元素。
????? ??? thisOrder.insertBefore(totalElement, thisOrder.getFirstChild());
???? // 將新元素添加到 Document ,指定新的 Node ,然后指定新 Node 在 Node 之前
????? ??
?? 3. 除去節點
???? Node deadNode = thisOrderItem.getParentNode().removeChild(thisOrderItem);
?
?? 4. 替換節點
???? Element backElement = doc.createElement("backordered"); ????
????? ??? // 創建新元素 backordered
????? ??? Node deadNode = thisOrderItem.getParentNode().replaceChild(backElement,thisOrderItem);
????? ??
?? 5. 創建和設置屬性
???? Element backElement = doc.createElement("backordered"); ????
????? ??? // 創建新元素 backordered
????? ??? backElement.setAttributeNode(doc.createAttribute("itemid"));
????? ??? // 創建新屬性 itemid
????? ??? String itemIdString = thisOrderItem.getAttributeNode("itemid").getNodeValue();
????? ??? // 取得 thisOrderItem 的屬性 itemid 的值
????? ??? backElement.setAttribute("itemid", itemIdString);
????? ??? // 設置 backElement 的屬性 item 的值 , 可以省略 createAttribute
????? ??? Node deadNode = thisOrderItem.getParentNode().replaceChild(backElement,thisOrderItem);
?
?? 6. 除去屬性
???? Element thisOrder = (Element)orders.item(orderNum);
????? ??? Element customer = (Element)thisOrder.getElementsByTagName("cusomertid").item(0);
????? ??? customer.removeAttribute("limit");
????? ??? // 去除屬性 limit
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
