使用Xerces-J的SAX方式进行XML程序设计

--SAX2(Simple API for XML)的结构


在本文中,笔者将首先介绍SAX Version 2 的使用方法,在以后介绍DOM Level 2 的使用方法。

SAX接口解析XML文件的基本原理为:首先获得一个实现了SAX接口的解析器,这个解析器通常是由第三方软件公司提供的;然后自己写一个符合SAX标准的处理器类,并且把这个类注册到刚刚做好的解析器;解析XML文件;解析器会把XML文件作为一个文件流读出来,然后再把文件流转换成一个事件流,这个事件流中的内容包括“文档开始”、“文档结束”、“元素开始”、“元素结束”等等标志文件状态的事件;根据事件调用注册到解析器里的处理器里面的相应的方法;在解析器进行XML文件的解析的同时,应用程序可以调用解析器提供的方法,对解析器的行为进行控制或获取解析器的状态。

SAX1发布于1998年五月,已经成为XML应用的世界标准,ibm/apache,sun,oracle,microsoft等知名大公司都要相应的解析器提供。SAX2接口是在2000年发布的,目前最高版本是SAX2release2pre。Ibm/apache已经有成熟的产品提供,microsoft的MSXML3.0 解析器也已经正式支持SAX2。sun的解析器目前已经处于预发布状态,oracle的XML解析器对SAX2的支持目前处于Beta状况。SAX2继承了SAX1的所有功能,仍然采用事件流/调用流的构架,但是SAX2同SAX1最大的几个区别在于:

  1. 用XmlReader取代了Parser。
  2. 支持名空间(Namespace),这个特性是很多XML的高级功能所必须的,同时这个特性也导致了在SAX接口很多部分的变化。
  3. 使用feature(特性)和properties(属性)的方法来配置解析器的行为和获取参数。通过XmlReader.setFeature(String name, boolean value )可以设置XmlReader的行为,通过XmlReader.getFeature( String name)可以获取当前的行为特性;通过XmlReader.setProperty( String name, Object value)可以设置XmlReader的参数,通过XmlReader.getProperty(String name)可以获取XmlReader的参数。
  4. 提出了Filter(过滤器)的概念,在SAX里,一个从应用程序到SAX的请求流和一个从SAX到应用程序的事件流组成了整个地交互过程。但是在某些情况下,我们需要对流中运行的信息进行修改,例如剔除某些不需要的信息,修改某些信息从而简化处理过程等等,这时在流中的信息到达Handler之前进行过滤就成了理所当然的选择。于是SAX2提出了接口XmlFilter。

在了解进一步的知识之前,首先我们需要了解的是,SAX不是一个XML的解析器,而是提供了一个通用的接口,这个接口使各种XML的解析器可以相互转换。这些支持SAX解析器具有相同的方法,并且可以通过Java的系统设置进行配置。

SAX2的基本构架最重要的组成部分是接口org.xml.sax.XMLReader和类org.xml.sax.helpers.DefaultHandler。在SAX2的架构里XMLReader取代了SAX1构架中的XMLParser,进行实际的XML文件的解析工作。类DefaultHandler实现了org.xml.sax.EntityResolver,org.xml.sax.DTDHandler,org.xml.sax.ContentHandler,org.xml.sax.ErrorHandler这四个接口,通过继承这个类,我们可以实现解析XML文件的所有任务。

下面的表1介绍了XMLReader中的主要方法及使用方法。表2介绍了另外一个很重要的接口DefaultHandler。

表1:XMLReader中的主要方法及使用方法
方法 使用说明
ContentHandler getContentHandler() 返回当前的内容处理器。
DTDHandler getDTDHandler() 返回当前的DTD处理器。
EntityResolver getEntityResolver() 返回当前的实体解析器。
ErrorHandler getErrorHandler() 返回当前的错误处理器。
boolean getFeature(java.lang.String name) 返回解析器的指定特性。参数为特性的名字,返回值为boolean值。关于特性的使用我们会在后面专门介绍。
Object getProperty(java.lang.String name) 返回解析器的指定属性。参数为属性的名字,这个字符串是可以自己创建的。关于属性的使用我们会在后面专门介绍。
void parse(InputSource input) 通过调用这个方法,应用程序可以通知解析器开始解析,参数为XML文件的来源。
void parse(java.lang.String systemId) 通过调用这个方法,应用程序可以通知解析器开始解析,参数为XML文件的来源。
void setContentHandler(ContentHandler handler) 设置内容处理器。
void setDTDHandler(DTDHandler handler) 设置DTD处理器。
void setEntityResolver(EntityResolver resolver) 设置实体解析器。
void setErrorHandler(ErrorHandler handler) 设置错误处理器。
void setFeature(java.lang.String name, boolean value) 设置特性。第一个参数为特性的名字,第二个参数为特性的值。
void setProperty(java.lang.String name, java.lang.Object value) 设置属性。第一个参数为属性的名字,第二个参数为属性的值。
表2:DefaultHandler的方法简介
方法 使用说明
public void characters(char[] ch, int start, int length) 当XML文档中出现字符串数据的时候这个方法被调用。第一个参数是包含字符串数据的字符数组,第二个参数是起始位置,第三个参数是长度。需要注意的是解析器可能把在一对tag内包含的字符串分成几个部分,连续调用charData方法若干次。
void endDocument() 当XML文档结束的时候这个方法被调用,这个方法每处理一个XML文档仅仅会被调用一次。
void endElement(java.lang.String uri,

java.lang.String localName,

java.lang.String qName)
当解析器在XML文件流中遇到一个Element的时候调用这个方法。第一个参数是名空间的URI,若没有则是一个空字符串;第二个参数是名空间的局域名,若没有则是一个空字符串,第三个参数为tag的名字。
void endPrefixMapping(String prefix) 当解析器遇到名空间映射结束的相应的tag的时候会调用这个方法。
void error(SAXParseException e)

处理一个可以修正的错误。
void fatalError(SAXParseException e) 处理一个不可修正的错误。
void ignorableWhitespace(char[] ch,

int start, int length)
当解析器在XML文件流中遇到可以忽略的空白的时候会调用这个方法,注意,非校验的XML解析器也有可能调用这个方法。
void notationDecl(java.lang.String name,

java.lang.String publicId,

java.lang.String systemId)
当解析器在XML文件流中遇到符号声明的时候调用这个方法,第一个参数为名称,第二个是publicId,第三个是systemId。
void processingInstruction(

java.lang.String target,

java.lang.String data)
当解析器在XML文件流中遇到处理指令的时候这个方法被调用。典型的处理指令如<?xml-stylesheet type="text/xml" href="14-2.xsl"?>。其中xml-stylesheet为第一个参数即target的内容,type="text/xml" href="14-2.xsl"为第二个参数即data中的内容。
void setDocumentLocator(Locator locator) 为解析器设置一个Locator,以便获取行号,列号等信息。
InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId) 解析器在打开一个外部的实体,例如DTD文件之前会调用这个方法,给应用程序开发者一个机会来修改其URI。如果返回null那么解析器将会按照原来的URI打开外部实体。
void skippedEntity(java.lang.String name) 解析器在XML文件流中发现不认识的实体时会调用这个方法。由于无校验解析不打开外部的DTD文件,这种情况通常在无校验解析地情况下遇到。
void startDocument() 文档开始时调用这个方法。
void startPrefixMapping(java.lang.String prefix, java.lang.String uri) 当解析器遇到名空间映射的时候会调用这个方法,注意,这个方法同对应的endPrefixMapping不一定成对调用。
void unparsedEntityDecl(java.lang.String name, java.lang.String

publicId, java.lang.String systemId, java.lang.String notationName)
当解析器遇到一个无法解析的实体声明的时候调用这个方法。
void warning(SAXParseException e) 当解析器遇到一个应该警告的状态时调用这个方法。

另外一个非常重要的类是org.xml.sax.helpers.XMLReaderFactory,这个类提供了创建一个XMLReader的方法。这个类只有两个方法,如表3。

 

表3:XMLReaderFactory中的方法
方法 使用说明
static XMLReader createXMLReader() 这个方法从系统属性org.xml.sax.driver中读取数据,然后建立一个XMLReader的实例。例如 java –Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser xxx xxx.xml 将会动态地建立一个org.apache.xerces.parsers.SAXParser的实例作为XMLReader。
static XMLReader createXMLReader(java.lang.String className) 用一个解析器类名字的字符串作为参数建立一个XMLReader的实例。

Xerces-J中的SAX接口的使用方法和我们曾经介绍的?LFRED的使用方法比较类似,Xerces-J的SAXParser是一个实现了XmlParser界面的类,在使用时我们首先需要设置相应的Handler,然后调用parse方法,这时Xerces-J会把XML文件当作一个流,从里面按照顺序把字符一个一个读出来,在遇到XML文件中的元素的时候会调用在解析文件前设置的Handler类中相应的Handler函数,对XML文件的内容要在Handler方法中进行处理。