當然和DOM也有非常相似的地方 都是先得到解析工廠 然後產生一個解析器一個是DOM的一個SAX的
SAX 的一大優點就是讀取和解析是同步的 不必像DOM那樣等樹分析完了再來操作
重要接口及方法
解析器
XMLReader SAX 2.0 的解析器必須實現這個接口 這個接口允許設置和查詢解析器的功能和屬性 註冊文檔處理的事件處理程序 以及開始文檔解析
void |
setFeature(String name, boolean value) 設置功能標誌的值。 |
boolean |
getFeature(String name) 查找功能標誌的值。 |
Object |
getProperty(String name) 查找屬性的值。 |
void |
setProperty(String name, Object value) 設置屬性的值。 |
void |
setDTDHandler(DTDHandler handler) 允許應用程序註冊 DTD 事件處理程序。 |
void |
setEntityResolver(EntityResolver resolver) 允許應用程序註冊實體分析器。 |
void |
setErrorHandler(ErrorHandler handler) 允許應用程序註冊錯誤事件處理程序。 |
void |
parse(InputSource input) 解析 XML 文檔。 |
等等
事件處理器
ContentHandler 實現XMLReader的解析器接口
void |
characters(char[] ch, int start, int length) 接收字符數據的通知。 |
void |
endDocument() 接收文檔的結尾的通知。 |
void |
endElement(String uri, String localName, String qName) 接收元素結束的通知。 |
void |
endPrefixMapping(String prefix) 結束前綴 URI 範圍的映射。 |
void |
ignorableWhitespace(char[] ch, int start, int length) 接收元素內容中可忽略的空白的通知。 |
void |
processingInstruction(String target, String data) 接收處理指令的通知。 |
void |
setDocumentLocator(Locator locator) 接收用來查找 SAX 文檔事件起源的對象。 |
void |
skippedEntity(String name) 接收跳過的實體的通知。 |
void |
startDocument() 接收文檔的開始的通知。 |
void |
startElement(String uri, String localName, String qName, Attributes atts) 接收元素開始的通知。 |
void |
startPrefixMapping(String prefix, String uri) 開始前綴 URI 名稱空間範圍映射。 |
SAX的API中定義了許多事件 這些事件分別由相應的事件處理器中相應的方法去實現去響應
JAXP中的解析器工廠和解析器
SAXParserFactory和SAXParser 其中SAXParser就是對XMLReader的實現的一個包裝類 裏面有各種Parse方法
一個實例 還是拿我的Hibernate.cfg.xml來玩
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/sshtest</property>
<property name="connection.username">root</property>
<property name="connection.password">00000000</property>
<!-- JDBC connection pool -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">120</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<!--<property name="hbm2ddl.auto">create</property>-->
<mapping resource="com/ergal/hibernate/pojo/User.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/Artist.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/Category.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/FileType.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/MyFile.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/MyLocation.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/MyPackage.hbm.xml"/>
<mapping resource="com/ergal/hibernate/pojo/MyPic.hbm.xml"/>
</session-factory>
</hibernate-configuration>
寫一個類 SAXTest.java
這裏是繼承了org.xml.sax.helpers的DefaultHandler這個事件處理的默認基類
DefaultHanlder是一個實現了ContentHandler, DTDHandler, EntityResolver, ErrorHandler 四個接口的輔助類
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
public class SaxTest extends DefaultHandler
{
public void startDocument() throws SAXException
{
//輸出XML聲明。
System.out.println("<?xml version='1.0' encoding='GBK'?>");
}
public void processingInstruction(String target,String data) throws SAXException
{
//輸出文檔中的處理指令。
System.out.print("<?"+target+" "+data+"?>");
}
public void startElement(String uri,String localName,
String qName,Attributes attrs)
throws SAXException
{
//輸出元素的開始標記及其屬性。
System.out.print("<"+qName);
int len=attrs.getLength();
for (int i = 0; i < len; i++)
{
System.out.print(" ");
System.out.print(attrs.getQName(i));
System.out.print("="");
System.out.print(attrs.getValue(i));
System.out.print(""");
}
System.out.print(">");
}
public void characters(char[] ch,int start,int length) throws SAXException
{
//輸出元素的字符數據內容。
System.out.print(new String(ch,start,length));
}
public void endElement(String uri,String localName,String qName) throws SAXException
{
//輸出元素的結束標記。
System.out.println("</"+qName+">");
}
public static void main(String[] args)
{
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp=null;
try
{
sp = spf.newSAXParser();
File file=new File("bin/hibernate.cfg.xml");
sp.parse(file,new SaxTest());
}
catch(ParserConfigurationException e){e.printStackTrace();}
catch(SAXException e){e.printStackTrace();}
catch(IOException e){e.printStackTrace();}
}
}
在Console輸出如下:
<hibernate-configuration><session-factory><property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/sshtest</property>
<property name="connection.username">root</property>
<property name="connection.password">00000000</property>
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">120</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<mapping resource="com/ergal/hibernate/pojo/User.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/Artist.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/Category.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/FileType.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/MyFile.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/MyLocation.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/MyPackage.hbm.xml"></mapping>
<mapping resource="com/ergal/hibernate/pojo/MyPic.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>
startDocument() 原來是一個空實現 這裏自己來把聲明寫出來 所以和hibernate.cfg.xml沒有什麼關係
processingInstruction(String target,String data) 也是空實現 並且我的XML裏沒有處理指令 所以也沒有具體的內容
target
- 處理指令目標。 data
- 處理指令數據,如果未提供,則爲 null。
startElement(String uri,String localName,
String qName,Attributes attrs) 元素開始的通知 也是空實現
uri
- 名稱空間 URI,如果元素沒有任何名稱空間 URI,或者沒有正在執行名稱空間處理,則爲空字符串。 localName
- 本地名稱(不帶前綴),如果沒有正在執行名稱空間處理,則爲空字符串。 qName
- 限定的名稱(帶有前綴),如果限定的名稱不可用,則爲空字符串。 attributes
- 附加到元素的屬性。如果沒有屬性,則它將是空的 Attributes 對象。
characters(char[] ch,int start,int length) 接收元素中字符數據的通知。也是空實現
ch
- 字符。 start
- 字符數組中的開始位置。 length
- 從字符數組中使用的字符數。
endElement(String uri,String localName,String qName) 接收元素結束的通知 也是空實現
uri
- 名稱空間 URI,如果元素沒有任何名稱空間 URI,或者沒有正在執行名稱空間處理,則爲空字符串。 localName
- 本地名稱(不帶前綴),如果沒有正在執行名稱空間處理,則爲空字符串。 qName
- 限定的名稱(帶有前綴),如果限定的名稱不可用,則爲空字符串
這些都是空實現 所以在程序中要重新寫入一些要用到的處理方法 並且這些方法都是基於回調的 比如
startElement(String uri,String localName,
String qName,Attributes attrs) 此方法就是在解析器在XML文檔的每個元素開始的時候自動的來調用這個方法 你可以在自己的類裏面定義一些處理程序 來處理數據
所以要用SAX的處理器處理XML 繼承DefaultHandler 然後重寫相應的方法即可
ErrorHandler 錯誤處理的接口 由於Defaulthanlder實現了這個接口 所以不詳細舉例
方法一樣是基於回調機制的 可以重寫以下方法
void |
warning(SAXParseException e) 接收解析器警告的通知。 |
void |
fatalError(SAXParseException e) 報告嚴重的 XML 解析錯誤。 |
void |
error(SAXParseException e) 接收可恢復的解析器錯誤的通知。 |
對錯誤進行處理 如打印錯誤和警告信息等
以下是一個例子用來用SAX來查找相對應的元素的值(轉自孫鑫的一個例子)
students.xml
<?xml-stylesheet type="text/xsl" href="students.xsl"?>
<students>
<student sn="01">
<name>張三</name>
<age>18</age>
</student>
<student sn="02">
<name>李四</name>
<age>20</age>
</student>
</students>
StudentsLookUp.java
import java.io.IOException;
import java.util.Stack;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
public class StudentLookup extends DefaultHandler
{
private Stack<String> tagsStack=new Stack<String>();
private String name;
private String age;
private String attrName;
private String attrValue;
/**
* 保存命令行參數傳入的屬性名字。
*/
public void setAttrName(String attrName)
{
this.attrName=attrName;
}
/**
* 保存命令行參數傳入的屬性的值。
*/
public void setAttrValue(String attrValue)
{
this.attrValue=attrValue;
}
public void startElement(String uri,String localName,
String qName,Attributes attrs)
throws SAXException
{
//判斷棧是否爲空,如果不爲空,說明已經找到了匹配的學生,
//於是將其子元素的名字入棧。
if(!tagsStack.empty())
{
tagsStack.push(qName);
}
else
{
//判斷是否是<student>元素,如果是,則取出該元素的所有屬性進行比對。
//如果找到匹配的學生,則將該元素的標記入棧。
if("student".equals(qName))
{
int len=attrs.getLength();
for(int i=0;i<len;i++)
{
if(attrName.equals(attrs.getQName(i))
&& attrValue.equals(attrs.getValue(i)))
{
tagsStack.push(qName);
break;
}
}
}
}
}
public void characters(char[] ch,int start,int length) throws SAXException
{
//判斷棧是否爲空,如果不爲空,說明已經找到了匹配的學生。
if(!tagsStack.empty())
{
//得到棧頂的對象,判斷是學生的姓名還是年齡,
//然後將字符數據保存到name或age變量中。
//JDk1.5以下版本,調用爲String tag=(String)tagsStack.peek();
String tag=tagsStack.peek();
if("name".equals(tag))
name=new String(ch,start,length);
if("age".equals(tag))
age=new String(ch,start,length);
}
}
public void endElement(String uri,String localName,String qName) throws SAXException
{
if(!tagsStack.empty())
{
//移除棧頂的對象。JDk1.5以下版本,調用爲String tag=(String)tagsStack.pop();
String tag=tagsStack.pop();
//判斷移除棧頂對象後,棧是否爲空,如果爲空,並且移除的棧頂對象是
//<student>元素的名字,則說明所有學生信息都已獲取完畢,於是打印輸出
//找到的學生信息,然後拋出SAXException異常,終止SAX解析器的解析過程。
if(tagsStack.empty() && "student".equals(tag))
{
System.out.println("name : "+name);
System.out.println("age : "+age);
throw new SAXException("找到匹配的學生。");
}
}
}
/**
* 如果endDocument()方法被調用,說明沒有找到匹配的學生。
*/
public void endDocument() throws SAXException
{
System.out.println("沒有找到匹配的學生。");
}
public static void main(String[] args)
{
if(args.length!=1)
{
System.out.println(" Usage: java StudentLookup key=value");
System.exit(1);
}
int index=args[0].indexOf("=");
if(-1==index)
{
System.out.println(" Usage: java StudentLookup key=value");
System.exit(1);
}
StudentLookup sl=new StudentLookup();
//從命令行參數中提取屬性的名字。
String str=args[0].substring(0,index);
sl.setAttrName(str);
//從命令行參數中提取屬性的值。
str=args[0].substring(index+1);
sl.setAttrValue(str);
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp=null;
try
{
sp = spf.newSAXParser();
File file=new File("students.xml");
sp.parse(file,sl);
}
catch(ParserConfigurationException e){System.out.println(e.getMessage());}
catch(SAXException e){System.out.println(e.getMessage());}
catch(IOException e){System.out.println(e.getMessage());}
}
}
執行程序時 用
當在元素的屬性檢查中遇到相同的屬性和值就將其 值儲存到Stack中(JDK 1.5中的泛型集合) 當遇到匹配的元素
就依次將元素的子元素名入Stack 然後在之後的字符數據處理中將此元素的數據賦值給預先定義的變量
然後在endElement方法中清空Stack 並拋出異常 同時終止程序