xml解析

java 代碼
  1. import javax.xml.parsers.DocumentBuilder;   
  2. import javax.xml.parsers.DocumentBuilderFactory;   
  3.   
  4. import org.w3c.dom.Document;   
  5. import org.w3c.dom.Element;   
  6. import org.w3c.dom.NodeList;   
  7.   
  8. public class xmldisplay {   
  9.     public static void main(String args[]) {   
  10.         try {   
  11.             DocumentBuilderFactory factory = DocumentBuilderFactory   
  12.                     .newInstance();   
  13.             DocumentBuilder builder = factory.newDocumentBuilder();   
  14.             Document doc = builder.parse("links.xml");   
  15.             doc.normalize();   
  16.             NodeList links = doc.getElementsByTagName("link");   
  17.             for (int i = 0; i < links.getLength(); i++) {   
  18.                 Element link = (Element) links.item(i);   
  19.                 System.out.print("Content: ");   
  20.                 System.out.println(link.getElementsByTagName("text").item(0)   
  21.                         .getFirstChild().getNodeValue());   
  22.                 System.out.print("URL: ");   
  23.                 System.out.println(link.getElementsByTagName("url").item(0)   
  24.                         .getFirstChild().getNodeValue());   
  25.                 System.out.print("Author: ");   
  26.                 System.out.println(link.getElementsByTagName("author").item(0)   
  27.                         .getFirstChild().getNodeValue());   
  28.                 System.out.print("Date: ");   
  29.                 Element linkdate = (Element) link.getElementsByTagName("date")   
  30.                         .item(0);   
  31.                 String day = linkdate.getElementsByTagName("day").item(0)   
  32.                         .getFirstChild().getNodeValue();   
  33.                 String month = linkdate.getElementsByTagName("month").item(0)   
  34.                         .getFirstChild().getNodeValue();   
  35.                 String year = linkdate.getElementsByTagName("year").item(0)   
  36.                         .getFirstChild().getNodeValue();   
  37.                 System.out.println(day + "-" + month + "-" + year);   
  38.                 System.out.print("Description: ");   
  39.                 System.out.println(link.getElementsByTagName("description")   
  40.                         .item(0).getFirstChild().getNodeValue());   
  41.                 System.out.println();   
  42.             }   
  43.         } catch (Exception e) {   
  44.             e.printStackTrace();   
  45.         }   
  46.   
  47.     }   
  48. }   
xml 代碼
  1. <!--sp-->xml version="1.0" encoding="UTF-8"?><links> 
  2.    
  3.     <link>  
  4.         <text/>  
  5.         <url>www..comurl>  
  6.         <author>hhauthor>  
  7.         <date>  
  8.             <day>23day>  
  9.             <month>7month>  
  10.             <year>2007year>  
  11.         date>  
  12.         <description>  
  13.             A site    
  14.         description>  
  15.     link>  

 

 如何利用JSP開發DOM應用?   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 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文檔名作爲輸入參數,返回一個Document對象,這個Document對象就代表了一個XML文檔的樹模型。以後所有的對XML文檔的操作,都與解析器無關,直接在這個Document對象上進行操作就可以了。而具體對Document操作的方法,就是由DOM所定義的了。   從得到的Document對象開始,我們就可以開始我們的DOM之旅了。使用Document對象的getElementsByTagName()方法,我們可以得到一個NodeList對象,一個Node對象代表了一個XML文檔中的一個標籤元素,而NodeList對象,觀其名而知其意,所代表的是一個Node對象的列表:   NodeList nl = doc.getElementsByTagName("message");   我們通過這樣一條語句所得到的是XML文檔中所有<message>標籤對應的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實例   先說說這個例子到底要做的是什麼吧,我們在一個名爲link.xml文件中保存了一些URL地址,我們希望可以通過DOM把這些URL讀出並顯示出來,也可以反過來向這個XML文件中寫入加入的URL地址。很簡單,卻很實用,也足夠來例示DOM的絕大部分用法了。   第一個程序我們稱爲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;i<links.getLength();i++){ Element link=(Element) links.item(i); System.out.print("Content: "); System.out.println(link.getElementsByTagName("text").item(0).getFirstChild(); .getNodeValue()); ……

  上面的代碼片斷就完成了對XML文檔內容的格式化輸出。只要注意到一些細節的問題,比如getFirstChile()方法和getElementsByTagName()方法的使用,這些還是比較容易的。   下面的內容,就是在修改了DOM樹後重新寫入到XML文檔中去的問題了。這個程序名爲xmlwrite.Java。在JAXP1.0版本中,並沒有直接的類和方法能夠處理XML文檔的寫入問題,需要藉助其它包中的一些輔助類。而在JAXP1.1版本中,引入了對XSLT的支持,所謂XSLT,就是對XML文檔進行變換(Translation)後,得到一個新的文檔結構。利用這個新加入的功能,我們就能夠很方便的把新生成或者修改後的DOM樹從新寫回到XML文件中去了,下面我們來看看代碼的實現,這段代碼的主要功能是向links.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="www.hzliu.com"; 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中所包含的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的內容就被寫入到文件中去了。

如何利用JSP開發SAX應用?   SAX是Simple API for XML的縮寫,它並不是由W3C官方所提出的標準,可以說是"民間"的事實標準。實際上,它是一種社區性質的討論產物。雖然如此,在XML中對SAX的應用絲毫不比DOM少,幾乎所有的XML解析器都會支持它。   與DOM比較而言,SAX是一種輕量型的方法。我們知道,在處理DOM的時候,我們需要讀入整個的XML文檔,然後在內存中創建DOM樹,生成DOM樹上的每個Node對象。當文檔比較小的時候,這不會造成什麼問題,但是一旦文檔大起來,處理DOM就會變得相當費時費力。特別是其對於內存的需求,也將是成倍的增長,以至於在某些應用中使用DOM是一件很不划算的事(比如在applet中)。這時候,一個較好的替代解決方法就是SAX。   SAX在概念上與DOM完全不同。首先,不同於DOM的文檔驅動,它是事件驅動的,也就是說,它並不需要讀入整個文檔,而文檔的讀入過程也就是SAX的解析過程。所謂事件驅動,是指一種基於回調(callback)機制的程序運行方法。(如果你對Java新的代理事件模型比較清楚的話,就會很容易理解這種機制了)在XMLReader接受XML文檔,在讀入XML文檔的過程中就進行解析,也就是說讀入文檔的過程和解析的過程是同時進行的,這和DOM區別很大。解析開始之前,需要向XMLReader註冊一個ContentHandler,也就是相當於一個事件監聽器,在ContentHandler中定義了很多方法,比如startDocument(),它定製了當在解析過程中,遇到文檔開始時應該處理的事情。當XMLReader讀到合適的內容,就會拋出相應的事件,並把這個事件的處理權代理給ContentHandler,調用其相應的方法進行響應。   這樣泛泛的說來或許有些不容易理解,別急,後面的例子會讓你明白SAX的解析過程。看看這個簡單XML文件:

<POEM> <AUTHOR>Ogden Nash</AUTHOR> <TITLE>Fleas</TITLE> <LINE>Adam</LINE> </POEM>
  當XMLReader讀到<POEM>標籤時,就會調用ContentHandler.startElement()方法,並把標籤名POEM作爲參數傳遞過去。在你實現的startElement()方法中需要做相應的動作,以處理當<POEM>出現時應該做的事情。各個事件隨着解析的過程(也就是文檔讀入的過程)一個個順序的被拋出,相應的方法也會被順序的調用,最後,當解析完成,方法都被調用後,對文檔的處理也就完成了。下面的這個表,列出了在解析上面的那個XML文件的時候,順序被調用的方法:   遇到的項目 方法回調
{文檔開始} startDocument() <POEM> startElement(null,"POEM",null,{Attributes}) "/n" characters("<POEM>/n...", 6, 1) <AUTHOR> startElement(null,"AUTHOR",null,{Attributes}) "Ogden Nash" characters("<POEM>/n...", 15, 10) </AUTHOR> endElement(null,"AUTHOR",null) "/n" characters("<POEM>/n...", 34, 1) <TITLE> startElement(null,"TITLE",null,{Attributes}) "Fleas" characters("<POEM>/n...", 42, 5) </TITLE> endElement(null,"TITLE",null) "/n" characters("<POEM>/n...", 55, 1) <LINE> startElement(null,"LINE",null,{Attributes}) "Adam" characters("<POEM>/n...", 62, 4) </LINE> endElement(null,"LINE",null) "/n" characters("<POEM>/n...", 67, 1) </POEM> endElement(null,"POEM",null) {文檔結束} endDocument()
  ContentHandler實際上是一個接口,當處理特定的XML文件的時候,就需要爲其創建一個實現了ContentHandler的類來處理特定的事件,可以說,這個實際上就是SAX處理XML文件的核心。下面我們來看看定義在其中的一些方法:   void characters(char[] ch, int start, int length):這個方法用來處理在XML文件中讀到字符串,它的參數是一個字符數組,以及讀到的這個字符串在這個數組中的起始位置和長度,我們可以很容易的用String類的一個構造方法來獲得這個字符串的String類:String charEncontered=new String(ch,start,length)。   void startDocument():當遇到文檔的開頭的時候,調用這個方法,可以在其中做一些預處理的工作。   void endDocument():和上面的方法相對應,當文檔結束的時候,調用這個方法,可以在其中做一些善後的工作。   void startElement(String namespaceURI, String localName, String qName, Attributes atts):當讀到一個開始標籤的時候,會觸發這個方法。在SAX1.0版本中並不支持名域,而在新的2.0版本中提供了對名域的支持,這兒參數中的namespaceURI就是名域,localName是標籤名,qName是標籤的修飾前綴,當沒有使用名域的時候,這兩個參數都未null。而atts是這個標籤所包含的屬性列表。通過atts,可以得到所有的屬性名和相應的值。要注意的是SAX中一個重要的特點就是它的流式處理,在遇到一個標籤的時候,它並不會紀錄下以前所碰到的標籤,也就是說,在startElement()方法中,所有你所知道的信息,就是標籤的名字和屬性,至於標籤的嵌套結構,上層標籤的名字,是否有子元屬等等其它與結構相關的信息,都是不得而知的,都需要你的程序來完成。這使得SAX在編程處理上沒有DOM來得那麼方便。   void endElement(String namespaceURI, String localName, String qName):這個方法和上面的方法相對應,在遇到結束標籤的時候,調用這個方法。   我們還是沿用講DOM的時候使用的那個文檔例子,但首先,我們先看一個簡單一些的應用,我們希望能夠統計一下XML文件中各個標籤出現的次數。這個例子很簡單,但是足以闡述SAX編程的基本思路了。一開始當然還是import語句了:
import org.xml.sax.helpers.DefaultHandler; import Javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import Java.util.*; import Java.io.*;
  然後,我們創建一個繼承於DefaultHandler的類,具體的程序邏輯在這兒可以暫且放在一邊,要注意的是程序的結構:
public class SAXCounter extends DefaultHandler { private Hashtable tags; //這個Hashtable用來記錄tag出現的次數 // 處理文檔前的工作 public void startDocument() throws SAXException { tags = new Hashtable();//初始化Hashtable } //對每一個開始元屬進行處理 public void startElement(String namespaceURI, String localName, String rawName, Attributes atts) throws SAXException { String key = localName; ……
  我們來看看這段程序作了些什麼。在main()方法中,主要做的就是創建解析器,然後解析文檔。實際上,在這兒創建SAXParser對象的時候,爲了使程序代碼於具體的解析器無關,使用了同DOM中一樣的設計技巧:通過一個SAXParserFactory類來創建具體的SAXParser對象,這樣,當需要使用不同的解析器的時候,要改變的,只是一個環境變量的值,而程序的代碼可以保持不變。這就是FactoryMethod模式的思想。在這兒不再具體講了,如果還有不明白的,可以參看上面DOM中的解釋,原理是一樣的。   不過在這兒還有一點點要注意的地方,就是SAXParser類和XMLReader類之間的關係。你可能有些迷糊了吧,實際上SAXParser是JAXP中對XMLReader的一個封裝類,而XMLReader是定義在SAX2.0種的一個用來解析文檔的接口。你可以同樣的調用SAXParser或者XMLReader中的parser()方法來解析文檔,效果是完全一樣的。不過在SAXParser中的parser()方法接受更多的參數,可以對不同的XML文檔數據源進行解析,因而使用起來要比XMLReader要方便一些。   這個例子僅僅涉及了SAX的一點皮毛,而下面的這個,可就要高級一些了。下面我們要實現的功能,在DOM的例子中已經有實現了,就是從XML文檔中讀出內容並格式化輸出,雖然程序邏輯看起來還是很簡單,但是SAX可不比DOM哦,看着吧。   前面說過,當遇到一個開始標籤的時候,在startElement()方法中,我們並不能夠得到這個標籤在XML文檔中所處的位置。這在處理XML文檔的時候是個大麻煩,因爲在XML中標籤的語義,有一部分是由其所處的位置所決定的。而且在一些需要驗證文檔結構的程序中,這更是一個問題。當然,沒有解決不了的問題了,我們可以使用一個棧來實現對文檔結構的紀錄。   棧的特點是先進先出,我們現在的想法是,在startElemnt()方法中用push將這個標籤的名字添加到棧中,在endElement()方法中在把它pop出來。我們知道對一個結構良好的XML而言,其嵌套結構是完備的,每一個開始標籤總會對應一個結束標籤,而且不會出現標籤嵌套之間的錯位。因而,每一次startElement()方法的調用,必然會對應一個endElement()方法的調用,這樣push和pop也是成對出現的,我們只需要分析棧的結構,就可以很容易的知道當前標籤所處在文檔結構中的位置了。
public class SAXReader extends DefaultHandler { Java.util.Stack tags=new Java.util.Stack(); ……
  在這兒雖然沒有使用到棧的分析,但實際上棧的分析是一件很容易的事情,應爲Java.util.Stack繼承了Java.util.Vector類,而且Stack中的元素是按棧的結構由底至上排列的,因個,我們可以使用Vector類的size()方法來得到Stack的元素個數,還可以使用Vector的get(int)方法來得到具體的每一個元屬。實際上,如果把Stack的元素從底向上逐一排列出來,我們就得到了從XML根節點到當前節點的一條唯一的路徑,有了這條路徑的信息,文檔的結構就在清楚不過了。   到目前爲止,我們已經掌握了對於XML編程的兩大利器:DOM和SAX,也知道了該如何在一個Java程序中使用它們。DOM編程相對簡單,但是速度比較慢,佔用內存多,而S AX編程複雜一些,但是速度快,佔用內存少。所以,我們應該根據不同的環境選擇使用不同的方法。大部分的XML應用基本都可以用它們來解決。需要特別說明的是,DOM和SAX其實都是語言無關的,並非Java所獨有,也就是說,只要有相應的語言實現,DOM和SAX可以應用在任何面向對象的語言中。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章