什么是 SAX
读取和操纵 XML 文件的标准方法是 DOM(“文档对象模型”)。遗憾的是,这种方法需要读取整个文件并将它存储到树结构中,因而效率不高、缓慢,并且会过度使用资源。

一种替代方法是使用 Simple API for XML 或 SAX。SAX 允许正在读取文档时处理该文档,这避免了在采取操作之前需要等待存储文档的所有内容。

SAX 是由 XML-DEV 邮件列表的成员开发的,Java 版本由 David Megginson 维护。他们的目的是提供一种更自然的方法来使用 XML,这种方法不会涉及到使用 DOM 的那种开销。

结果是基于事件的 API。解析器将事件(譬如,元素的开始或结束)发送给处理信息的事件处理程序。然后,应用程序自己可以处理数据。虽然原始文档保持不变,但 SAX 提供了操纵数据的方法,然后会将该方法导向另一个过程或文档。

对于 SAX,没有官方的标准;万维网(W3C)或其它官方组织不维护 SAX,但在 XML 社区中,它是一个事实上的标准。

SAX 处理是如何工作的
SAX 分析经过其的 XML 流,这非常象老式的自动收报机纸条。考虑以下 XML 代码片断:


<?xml version="1.0"?>
<samples>
<server>UNIX</server>
<monitor>color</monitor>
</samples>

一般情况下,SAX 处理器分析这段代码将生成以下事件:


Start document
Start element (samples)
Characters (white space)
Start element (server)
Characters (UNIX)
End element (server)
Characters (white space)
Start element (monitor)
Characters (color)
End element (monitor)
Characters (white space)
End element (samples)

SAX API 允许开发者捕获这些事件,并对它们进行操作。

SAX 处理涉及以下几步:

  1. 创建事件处理程序。
  2. 创建 SAX 解析器。
  3. 将事件处理程序分配给解析器。
  4. 对文档进行解析,将每个事件发送给处理程序。

在SAX与DOM之间,如何选择
选择 DOM 还是 SAX,这取决于几个因素:

  1. 应用程序的目的:如果必须对数据进行更改,并且作为 XML 将它输出,则在大多数情况下,使用 DOM。与使用 XSL 转换来完成的简单结构更改不一样,如果是对数据本身进行更改,则尤其应该使用 DOM。
  2. 数据的数量:对于大文件,SAX 是更好的选择。
  3. 将如何使用数据:如果实际上只使用一小部分数据,则使用 SAX 将数据抽取到应用程序中,这种方法更好些。另一方面,如果知道将需要向后引用已经处理过的信息,则 SAX 可能不是正确的选择。
  4. 需要速度:通常,SAX 实现比 DOM 实现快。

记住 SAX 和 DOM 不是互斥的,这一点很重要。可以使用 DOM 来创建事件的 SAX 流,可以使用 SAX 来创建 DOM 树。事实上,大多数解析器实际常常使用 SAX 来创建 DOM 树!

使用 JAXP 来创建解析器
接下来我们来看一下JAXP的SAX Parser是怎么样工作的。

首先声明 XMLReader xmlReader。然后使用 SAXParserFactory 来创建 SAXParser。正是 SAXParser 给您了 XMLReader。


import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.XMLReader;

public class SurveyReader extends DefaultHandler
{
public SurveyReader() {
}

public static void main (String args[]) {
XMLReader xmlReader = null;
try {
SAXParserFactory spfactory =
SAXParserFactory.newInstance();
spfactory.setValidating(false); //设置验证选项,
如果您的XML文件是有效文档的话,
就不用验证.这将提高处理速度
SAXParser saxParser = spfactory.newSAXParser();
xmlReader = saxParser.getXMLReader();

} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}

设置内容处理程序
一旦创建了解析器,则需要将 SurveyReader 设置为内容处理程序,以便于其接收事件。

xmlReader 的 setContentHandler() 方法完成这项工作。


...
xmlReader = saxParser.getXMLReader();

xmlReader.setContentHandler(new SurveyReader());

} catch (Exception e) {
...

当然,对于内容处理程序,这不是唯一的选项。

解析 InputSource
为了对文件进行实际地解析,需要 InputSource。这个 SAX 类封装了所有将要处理的数据,所以不必担心它来自哪里。

现在,准备对文件进行实际解析。应用程序将封装在 InputSource 中的文件传递给 parse(),然后应用程序会继续运行。


...

import org.xml.sax.InputSource;

...
xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new SurveyReader());

InputSource source = new InputSource("surveys.xml");
xmlReader.parse(source);


} catch (Exception e) {
...

可以编译和运行该程序,但这时应该什么也没有发生,因为应用程序还没有定义任何事件。

创建 ErrorHandler
当然总会有可能在试图进行解析时,数据有问题。在这样的情况下,有一个处理程序来处理错误和内容将是有帮助的。

就如同创建内容处理程序一样,可以创建出错处理程序。通常,将作为 ErrorHandler 的单独实例来创建它,但为了简化该示例,出错处理正是包含在 SurveyResults 中。由于该类继承了 DefaultHandler 且没有扩展 ContentHandler,所以这种双重用法是可能的。

需要关注的事件有三个:警告、错误和致命错误。


...

import org.xml.sax.SAXParseException;


public class SurveyReader extends DefaultHandler
{

public SurveyReader() {
}
public void error (SAXParseException e) {
System.out.println("Error parsing the file: "+e.getMessage());
}
public void warning (SAXParseException e) {
System.out.println("Problem parsing the file: "+e.getMessage());
}
public void fatalError (SAXParseException e) {
System.out.println("Error parsing the file: "+e.getMessage());
System.out.println("Cannot continue.");
System.exit(1);
}
}
....
xmlReader.setContentHandler(new SurveyReader());

xmlReader.setErrorHandler(new SurveyReader());
//设置 ErrorHandler

InputSource source = new InputSource("surveys.xml");
....

public static void main (String args[]) {
...

SAX 事件
以下事件是常用的;它们都在 org.xml.sax 包的 HandlerBase 类中定义。

  1. startDocument 表示文档开始。
  2. endDocument 表示文档结束。
  3. startElement 表示元素开始。当一对标记中的起始标记中的所有内容被处理后,解析器 激发此事件。包括了标记名和其属性。
  4. endElement 表示元素结束。
  5. characters 包含字符数据,类似于 DOM 的一个 Text 节点。

还有更多的 SAX 事件:

  1. ignorableWhitespace 此事件类似于我们前面所讨论的无用 DOM 节点。它与 character 事件的区别,好处是:如果您不需要空格符,您可以通过忽略这个事件来忽略所有的空格符。
  2. warning、error 和 fatalError 这三个事件表示了解析错误。您可根据需要来响应它们。
  3. setDocumentLocator 这个事件允许您存储一个 SAX 的 Locator 对象。Locator 对象可以用来找出在文档中确切发生事件的地方。

转自:http://www-128.ibm.com/developerworks/cn/xml/x-cert/part8/