Sax解析xml_动力节点Java学院整理
JAVA解析XML通常有两种方式,DOM 和 SAX。DOM虽然是W3C的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一个驻留内存的树结构(节点树),然后您的代码才可以使用DOM的标准接口来操作这个树结构。但大部分情况下我们只对文档的部分内容感兴趣,根本就不用先解析整个文档,并且从节点树的根节点来索引一些我们需要的数据也是非常耗时的。
SAX是一种XML解析的替代方法。相比于文档对象模型DOM,SAX是读取和操作XML数据的更快速、更轻量的方法。SAX允许您在读取文档时处理它,从而不必等待整个文档被存储之后才采取操作。它不涉及DOM所必需的开销和概念跳跃。 SAXAPI是一个基于事件的API ,适用于处理数据流,即随着数据的流动而依次处理数据。SAXAPI 在其解析您的文档时发生一定事件的时候会通知您。在您对其响应时,您不作保存的数据将会被抛弃。
下面是一个SAX解析XML的示例(有点长,因为详细注解了SAX事件处理的所有方法),SAXAPI中主要有四种处理事件的接口,它们分别是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler 。下面的例子可能有点冗长,实际上只要继承DefaultHandler 类,再覆盖一部分处理事件的方法同样可以达到这个示例的效果,但为了纵观全局,还是看看SAXAPI里面所有主要的事件解析方法吧。(实际上DefaultHandler就是实现了上面的四个事件处理器接口,然后提供了每个抽象方法的默认实现。)
1,ContentHandler 接口:接收文档逻辑内容的通知的处理器接口。
importorg.xml.sax.Attributes;
importorg.xml.sax.ContentHandler;
importorg.xml.sax.Locator;
importorg.xml.sax.SAXException;
classMyContentHandlerimplementsContentHandler{
StringBufferjsonStringBuffer;
intfrontBlankCount=0;
publicMyContentHandler(){
jsonStringBuffer=newStringBuffer();
}
/*
*接收字符数据的通知。
*在DOM中ch[begin:end]相当于Text节点的节点值(nodeValue)
*/
@Override
publicvoidcharacters(char[]ch,intbegin,intlength)throwsSAXException{
StringBufferbuffer=newStringBuffer();
for(inti=begin;i>>characters("+length+"):"+buffertoString());
}
/*
*接收文档的结尾的通知。
*/
@Override
publicvoidendDocument()throwsSAXException{
System.out.println(this.toBlankString(--this.frontBlankCount)+
">>>enddocument");
}
/*
*接收文档的结尾的通知。
*参数意义如下:
*uri:元素的命名空间
*localName:元素的本地名称(不带前缀)
*qName:元素的限定名(带前缀)
*
*/
@Override
publicvoidendElement(Stringuri,StringlocalName,StringqName)
throwsSAXException{
System.out.println(this.toBlankString(--this.frontBlankCount)+
">>>endelement:"+qName+"("+uri+")");
}
/*
*结束前缀URI范围的映射。
*/
@Override
publicvoidendPrefixMapping(Stringprefix)throwsSAXException{
System.out.println(this.toBlankString(--this.frontBlankCount)+
">>>endprefix_mapping:"+prefix);
}
/*
*接收元素内容中可忽略的空白的通知。
*参数意义如下:
*ch:来自XML文档的字符
*start:数组中的开始位置
*length:从数组中读取的字符的个数
*/
@Override
publicvoidignorableWhitespace(char[]ch,intbegin,intlength)
throwsSAXException{
StringBufferbuffer=newStringBuffer();
for(inti=begin;i>>ignorablewhitespace("+length+"):"+buffer.toString());
}
/*
*接收处理指令的通知。
*参数意义如下:
*target:处理指令目标
*data:处理指令数据,如果未提供,则为null。
*/
@Override
publicvoidprocessingInstruction(Stringtarget,Stringdata)
throwsSAXException{
System.out.println(this.toBlankString(this.frontBlankCount)+">>>processinstruction:(target=\""
+target+"\",data=\""+data+"\")");
}
/*
*接收用来查找SAX文档事件起源的对象。
*参数意义如下:
*locator:可以返回任何SAX文档事件位置的对象
*/
@Override
publicvoidsetDocumentLocator(Locatorlocator){
System.out.println(this.toBlankString(this.frontBlankCount)+
">>>setdocument_locator:(lineNumber="+locatorgetLineNumber()
+",columnNumber="+locatorgetColumnNumber()
+",systemId="+locatorgetSystemId()
+",publicId="+locatorgetPublicId()+")");
}
/*
*接收跳过的实体的通知。
*参数意义如下:
*name:所跳过的实体的名称。如果它是参数实体,则名称将以'%'开头,
*如果它是外部DTD子集,则将是字符串"[dtd]"
*/
@Override
publicvoidskippedEntity(Stringname)throwsSAXException{
System.out.println(this.toBlankString(this.frontBlankCount)+
">>>skipped_entity:"+name);
}
/*
*接收文档的开始的通知。
*/
@Override
publicvoidstartDocument()throwsSAXException{
System.out.println(this.toBlankString(this.frontBlankCount++)+
">>>startdocument");
}
/*
*接收元素开始的通知。
*参数意义如下:
*uri:元素的命名空间
*localName:元素的本地名称(不带前缀)
*qName:元素的限定名(带前缀)
*atts:元素的属性集合
*/
@Override
publicvoidstartElement(Stringuri,StringlocalName,StringqName,
Attributesatts)throwsSAXException{
System.out.println(this.toBlankString(this.frontBlankCount++)+
">>>startelement:"+qName+"("+uri+")");
}
/*
*开始前缀URI名称空间范围映射。
*此事件的信息对于常规的命名空间处理并非必需:
*当http://xmlorg/sax/features/namespaces功能为true(默认)时,
*SAXXML读取器将自动替换元素和属性名称的前缀。
*参数意义如下:
*prefix:前缀
*uri:命名空间
*/
@Override
publicvoidstartPrefixMapping(Stringprefix,Stringuri)
throwsSAXException{
System.out.println(this.toBlankString(this.frontBlankCount++)+
">>>startprefix_mapping:xmlns:"+prefix+"="
+"\""+uri+"\"");
}
privateStringtoBlankString(intcount){
StringBufferbuffer=newStringBuffer();
for(inti=0;i
2,DTDHandler 接口:接收与DTD相关的事件的通知的处理器接口。
importorg.xml.sax.DTDHandler;
importorg.xml.sax.SAXException;
publicclassMyDTDHandlerimplementsDTDHandler{
/*
*接收注释声明事件的通知。
*参数意义如下:
*name-注释名称。
*publicId-注释的公共标识符,如果未提供,则为null。
*systemId-注释的系统标识符,如果未提供,则为null。
*/
@Override
publicvoidnotationDecl(Stringname,StringpublicId,StringsystemId)
throwsSAXException{
Systemoutprintln(">>>notationdeclare:(name="+name
+",systemId="+publicId
+",publicId="+systemId+")");
}
/*
*接收未解析的实体声明事件的通知。
*参数意义如下:
*name-未解析的实体的名称。
*publicId-实体的公共标识符,如果未提供,则为null。
*systemId-实体的系统标识符。
*notationName-相关注释的名称。
*/
@Override
publicvoidunparsedEntityDecl(Stringname,
StringpublicId,
StringsystemId,
StringnotationName)throwsSAXException{
Systemoutprintln(">>>unparsedentitydeclare:(name="+name
+",systemId="+publicId
+",publicId="+systemId
+",notationName="+notationName+")");
}
}
3,EntityResolver 接口:是用于解析实体的基本接口。
importjava.io.IOException;
importorg.xml.sax.EntityResolver;
importorg.xml.sax.InputSource;
importorg.xml.sax.SAXException;
publicclassMyEntityResolverimplementsEntityResolver{
/*
*允许应用程序解析外部实体。
*解析器将在打开任何外部实体(顶级文档实体除外)前调用此方法
*参数意义如下:
*publicId:被引用的外部实体的公共标识符,如果未提供,则为null。
*systemId:被引用的外部实体的系统标识符。
*返回:
*一个描述新输入源的InputSource对象,或者返回null,
*以请求解析器打开到系统标识符的常规URI连接。
*/
@Override
publicInputSourceresolveEntity(StringpublicId,StringsystemId)
throwsSAXException,IOException{
returnnull;
}
}
4,ErrorHandler接口:是错误处理程序的基本接口。
importorg.xml.sax.ErrorHandler;
importorg.xml.sax.SAXException;
importorg.xml.sax.SAXParseException;
publicclassMyErrorHandlerimplementsErrorHandler{
/*
*接收可恢复的错误的通知
*/
@Override
publicvoiderror(SAXParseExceptione)throwsSAXException{
System.err.println("Error("+e.getLineNumber()+","
+e.getColumnNumber()+"):"+e.getMessage());
}
/*
*接收不可恢复的错误的通知。
*/
@Override
publicvoidfatalError(SAXParseExceptione)throwsSAXException{
System.err.println("FatalError("+e.getLineNumber()+","
+e.getColumnNumber()+"):"+e.getMessage());
}
/*
*接收不可恢复的错误的通知。
*/
@Override
publicvoidwarning(SAXParseExceptione)throwsSAXException{
System.err.println("Warning("+e.getLineNumber()+","
+e.getColumnNumber()+"):"+e.getMessage());
}
}
Test 类的主方法打印解析books.xml时的事件信息。
importjava.io.FileNotFoundException;
importjava.io.FileReader;
importjava.io.IOException;
importorg.xml.sax.ContentHandler;
importorg.xml.sax.DTDHandler;
importorg.xml.sax.EntityResolver;
importorg.xml.sax.ErrorHandler;
importorg.xml.sax.InputSource;
importorg.xml.sax.SAXException;
importorg.xml.sax.XMLReader;
importorg.xml.sax.helpers.XMLReaderFactory;
publicclassTest{
publicstaticvoidmain(String[]args)throwsSAXException,
FileNotFoundException,IOException{
//创建处理文档内容相关事件的处理器
ContentHandlercontentHandler=newMyContentHandler();
//创建处理错误事件处理器
ErrorHandlererrorHandler=newMyErrorHandler();
//创建处理DTD相关事件的处理器
DTDHandlerdtdHandler=newMyDTDHandler();
//创建实体解析器
EntityResolverentityResolver=newMyEntityResolver();
//创建一个XML解析器(通过SAX方式读取解析XML)
XMLReaderreader=XMLReaderFactory.createXMLReader();
/*
*设置解析器的相关特性
*http://xml.org/sax/features/validation=true表示开启验证特性
*http://xml.org/sax/features/namespaces=true表示开启命名空间特性
*/
reader.setFeature("http://xml.org/sax/features/validation",true);
reader.setFeature("http://xml.org/sax/features/namespaces",true);
//设置XML解析器的处理文档内容相关事件的处理器
reader.setContentHandler(contentHandler);
//设置XML解析器的处理错误事件处理器
reader.setErrorHandler(errorHandler);
//设置XML解析器的处理DTD相关事件的处理器
reader.setDTDHandler(dtdHandler);
//设置XML解析器的实体解析器
reader.setEntityResolver(entityResolver);
//解析books.xml文档
reader.parse(newInputSource(newFileReader("books.xml")));
}
}
books.xml 文件的内容如下:
ThinkinginJAVA
CoreJAVA2
C++primer
控制台输出如下:
>>>setdocument_locator:(lineNumber=1,columnNumber=1,systemId=null,publicId=null)
>>>startdocument
Error(2,7):Documentisinvalid:nogrammarfound.
Error(2,7):Documentrootelement"books",mustmatchDOCTYPEroot"null".
>>>startprefix_mapping:xmlns:="http://test.org/books"
>>>startelement:books(http://test.org/books)
>>>characters(2):\n\t
>>>characters(2):\n\t
>>>startelement:book(http://test.org/books)
>>>characters(3):\n\t\t
>>>startelement:name(http://test.org/books)
>>>characters(16):ThinkinginJAVA
>>>endelement:name(http://test.org/books)
>>>characters(2):\n\t
>>>endelement:book(http://test.org/books)
>>>characters(2):\n\t
>>>startelement:book(http://test.org/books)
>>>characters(3):\n\t\t
>>>startelement:name(http://test.org/books)
>>>characters(10):CoreJAVA2
>>>endelement:name(http://test.org/books)
>>>characters(2):\n\t
>>>endelement:book(http://test.org/books)
>>>characters(2):\n\t
>>>startelement:book(http://test.org/books)
>>>characters(3):\n\t\t
>>>startelement:name(http://test.org/books)
>>>characters(10):C++primer
>>>endelement:name(http://test.org/books)
>>>characters(2):\n\t
>>>endelement:book(http://test.org/books)
>>>characters(1):\n
>>>endelement:books(http://test.org/books)
>>>endprefix_mapping:
>>>enddocument