Posted on 2009-12-20 14:49
啥都寫點 閱讀(337)
評論(0) 編輯 收藏 所屬分類:
J2SE
用DOM處理XML文檔時,需要讀取整個XML文檔,然后在內存中創建DOM樹,生成DOM樹上的每個Node對象。當XML文檔很大時,需要的內存也就很大,開銷較大。本例介紹另一種輕量級的處理XML文檔的方法:SAX(Simple API for XML),將描述學生信息的XML文檔的內容解析成多個學生對象。

SAX不同于DOM的文檔驅動,它是事件驅動(基于回調機制)的,即SAX不需要讀入整個文檔,文檔的讀入過程也就是SAX的解析過程。

java.xml.parsers.SAXParser是SAX解析器,由SAX解析器工廠SAXParserFactory的newSAXParser方法創建,SAXParser的parse方法解析XML文檔。

必須為SAXParser指定事件偵聽器對象,它必須繼承DefaultHandler,程序員必須按需重寫DefaultHander的一些方法,這是SAX解析XML文檔的核心,常常需要被重寫的方法如下:

startDocument方法:當SAX解析器讀到文檔開頭的內容時,調用該方法。

endDocument方法:當SAX解析器讀到文檔結束的內容時,調用該方法。

startElement方法:當SAX解析器讀到標簽開始的內容時,調用該方法。

endElement方法:當SAX解析器讀到標簽結束的內容時,調用該方法。

characters方法:當SAX解析器讀到標簽中的文本內容時,調用該方法。


/** *//**------------------------------------------SaxXML.java-------------------------------------------------*/
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


/** *//**
* 使用SAX處理XML文檔。SAX是Simple API for XML的縮寫。
* 與DOM比較而言,SAX是一種輕量型的方法。我們知道,在處理DOM的時候,我們需要讀入整個的XML文檔,然后在內存中創建DOM樹,生成DOM樹上的每個Node對象。當文檔比較小的時候,這不會造成什么問題,但是一旦文檔大起來,處理DOM就會變得相當費時費力。特別是其對于內存的需求,也將是成倍的增長,以至于在某些應用中使用DOM是一件很不劃算的事(比如在applet中)。這時候,一個較好的替代解決方法就是SAX。
* SAX在概念上與DOM完全不同。首先,不同于DOM的文檔驅動,它是事件驅動的,也就是說,它并不需要讀入整個文檔,而文檔的讀入過程也就是SAX的解析過程。所謂事件驅動,是指一種基于回調(callback)機制的程序運行方法。
*/

public class SaxXML
{


public static List readXML(String fileName) throws Exception
{
// 創建SAX解析器工廠對象
SAXParserFactory spf = SAXParserFactory.newInstance();
// 使用解析器工廠創建解析器實例
SAXParser saxParser = spf.newSAXParser();

// 創建SAX解析器要使用的事件偵聽器對象
StudentSAXHandler handler = new StudentSAXHandler();
// 開始解析文件
saxParser.parse(new File(fileName), handler);

// 獲取結果
return handler.getResult();
}


public static void main(String[] args)
{

String filename = "students.xml";
List studentBeans = null;

try
{
studentBeans = SaxXML.readXML(filename);

} catch (Exception e)
{
System.err.println(e.getMessage());
}

if (studentBeans != null)
{
System.out.println("解析student.xml文檔得到的學生信息:");

for (int i = 0; i < studentBeans.size(); i++)
{
System.out.println(studentBeans.get(i).toString());
}
}
}


/** *//**
* SAX的事件偵聽器,當處理特定的XML文件的時候,
* 就需要為其創建一個實現了ContentHandler的類來處理特定的事件,
* 可以說,這個實際上就是SAX處理XML文件的核心。
*/

static class StudentSAXHandler extends DefaultHandler
{
// 保存已經讀到過但還沒有關閉的標簽。
java.util.Stack tagsStatck = new java.util.Stack();
List studentBeans = new ArrayList();
StudentBean bean = null;


/** *//**
* 當遇到文檔的開頭的時候,調用這個方法,可以在其中做一些預處理的工作
*/

public void startDocument() throws SAXException
{
System.out.println("------Parse begin--------");
}


/** *//**
* 當文檔結束的時候,調用這個方法,可以在其中做一些善后的工作
*/

public void endDocument() throws SAXException
{
System.out.println("------Parse end--------");
}

/** *//**
* 當讀到一個開始標簽的時候,會觸發這個方法.
* namespaceURI就是名域,localName是標簽名,qName是標簽的修飾前綴,
* atts是這個標簽所包含的屬性列表。通過atts,可以得到所有的屬性名和相應的值.
* <name="">
*/
public void startElement(String namespaceURI, String localName, String qName, Attributes atts)

throws SAXException
{
tagsStatck.push(qName);
// 如果新的標簽是“學生”,則表示接下來要讀取學生。這里之所以需要bean為空,是因為放置學生標簽的子標簽也有“學生”

if (bean == null)
{

if (qName.equals("學生"))
{
System.out.println("------Processing a student--------");
bean = new StudentBean();
bean.setGender(atts.getValue("性別"));
}
}
}


/** *//**
* 在遇到結束標簽的時候,調用這個方法
*/
public void endElement(String namespaceURI, String localName, String qName)

throws SAXException
{
// 將最近讀取的標簽彈出
String currenttag = (String)tagsStatck.pop();
// 最近讀到的標簽應該與即將關閉的標簽一樣。

if (!currenttag.equals(qName))
{
throw new SAXException("XML文檔格式不正確,標簽不匹配!");
}
// 如果關閉的是"學生"標簽,則表示一個StudentBean已經構造完畢了。

if (qName.equals("學生"))
{
System.out.println("------Processing a student end--------");
// 將bean實例放入學生列表中,同時置空,等待構造下一個實例
studentBeans.add(bean);
bean = null;
}
}


/** *//**
* 處理在XML文件中讀到字符串
* @see org.xml.sax.ContentHandler#characters(char[], int, int)
*/

public void characters(char[] chs, int start, int length) throws SAXException
{
// 從棧中得到當前節點的信息
String tag = (String) tagsStatck.peek();
String value = new String(chs, start, length);

if (tag.equals("姓名"))
{
// 如果最近讀到的標簽是姓名,則把字符串當作姓名的值
bean.setName(value);

} else if (tag.equals("年齡"))
{
bean.setAge(Integer.parseInt(value));

} else if (tag.equals("電話"))
{
bean.setPhone(value);
}
}

public List getResult()
{
return studentBeans;
}
}
}
--
學海無涯