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