当然和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 并抛出异常 同时终止程序