SAX入门

来源:互联网 发布:椰族部落永久域名新址 编辑:程序博客网 时间:2024/04/30 12:17
wukejia@msn.com

本文为打算在程序中使用SAX2技术的Java程序员提供一个快速指南。

1 必备条件


SAX是一个针对多种不同XML解析器(以及具备XML解析器行为的东西)实现的通用接口。就像JDBC是一个针对不同关系数据库(以及看起来象关系数据库一样的东东)实现的接口一样。如果你打算使用SAX,以下前提是必须的:

(1)Java 1.1 或其更高版本。
(2)一个SAX2兼容的XML解析器,它应当被安装在Java classpath中。
(3)SAX2的发布包(通常由XML解析器提供)应当安装在Java classpath中。大多数的Java/XML工具都会包括SAX2以及一个使用它的解析器。

绝大多数的web应用服务器使用SAX2做为它们对XML的核心支持。尤其是具备JAXP 1.1支持的环境通常都提供SAX2。

2 解析一个文档


首先,创建一个继承自DefaultHandler的类:

import org.xml.sax.helpers.DefaultHandler;

public class MySAXApp extends DefaultHandler
{

    public MySAXApp ()
    {
    super();
    }

}

因为这是个Java程序,我们创建一个main方法,使用XMLReaderFactory的createXMLReader方法动态选取一个SAX驱动。注意此处的“throws Exception”只是象征性的,真正的程序中不应出现这么简陋的异常处理代码:

public static void main (String args[])
    throws Exception
{
    XMLReader xr = XMLReaderFactory.createXMLReader();
}


如果你的Java环境无法提供一个默认的编译时XML驱动(或者使用系统资源META-INF/services/org.xl.sax.driver),那么你有必要设置Java系统属性org.xml.sax.driver的值为SAX驱动的全类名,比如:

java -Dorg.xml.sax.driver=com.example.xml.SAXDriver MySAXApp sample.xml

你可能使用到的类名有:

       Class Name                                                  Notes
gnu.xml.aelfred2.SAXDriver                        Lightweight non-validating parser; Free Software
gnu.xml.aelfred2.XmlReader                                  Optionally validates; Free Software
oracle.xml.parser.v2.SAXParser                             Optionally validates; proprietary
org.apache.crimson.parser.XMLReaderImpl      Optionally validates; used in JDK 1.4; Open Source
org.apache.xerces.parsers.SAXParser                   Optionally validates; Open Source

或者,如果你不介意把你的程序绑定到一个唯一的SAX驱动,你可以直接使用它的构造函数:

public static void main (String args[])
    throws Exception
{
    XMLReader xr = new com.example.xml.SAXDriver();
}


这里我们假设用到的XML解析器的SAX驱动名叫com.example.xml.SAXDriver(实际上不存在)。我们能够使用这个对象来解析XML文档,但首先,我们必须注册解析器用来报告信息的事件处理器,这通过XMLReader接口的setContentHandler方法和setErrorHandler方法实现。在真实的应用中处理器通常是分别独立的对象,但对这个简单的演示,我们把所有的处理器绑定到顶级类(MySAXApp),我们实例化这个类,并把它注册给XML读取对象:

    public static void main (String args[])
    throws Exception
    {
        XMLReader xr = XMLReaderFactory.createXMLReader();
        MySAXApp handler = new MySAXApp();
        xr.setContentHandler(handler);
        xr.setErrorHandler(handler);
    }


以上代码创建一个MySAXApp的实例来接收XML解析事件,并把它注册给XML读取器以接收规范的内容事件和错误事件(还有其它极少用到的事件)。现在,让我们假定所有的命令行参数都是文件名,我们尝试用XMLReader接口的parse方法一个一个地解析它们:

    public static void main (String args[])
    throws Exception
    {
        XMLReader xr = XMLReaderFactory.createXMLReader();
        MySAXApp handler = new MySAXApp();
        xr.setContentHandler(handler);
        xr.setErrorHandler(handler);
        // Parse each file provided on the command line.
        for (int i = 0; i < args.length; i++) {
            FileReader r = new FileReader(args[i]);
            xr.parse(new InputSource(r));
        }
    }


注意,每一个XML资源必须被包装到InputSource对象中来被解析。下面是完整的演示代码(到目前为止):

import java.io.FileReader;

import org.xml.sax.XMLReader;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;


public class MySAXApp extends DefaultHandler
{

    public static void main (String args[])
    throws Exception
    {
    XMLReader xr = XMLReaderFactory.createXMLReader();
    MySAXApp handler = new MySAXApp();
    xr.setContentHandler(handler);
    xr.setErrorHandler(handler);

    // Parse each file provided on the command line.
    for (int i = 0; i < args.length; i++) {
        FileReader r = new FileReader(args[i]);
        xr.parse(new InputSource(r));
    }
    }


    public MySAXApp ()
    {
    super();
    }
}


你可以编译这个代码并运行它(记得指定org.xml.sax.driver属性为SAX驱动),但除非文档包含没有定义良好的XML,程序不会输出任何信息,因为你还没有设置你的程序来处理SAX事件。

3 处理事件


当你开始实现XML解析事件的处理方法时,情况开始变得有趣(记得在前面一段我们注册了一个类来接收XML解析事件)。最重要的事件是文档的开始和结束,元素的开始和结束以及字符数据。

为了找出文档的开始和结束,客户程序实现startDocument和endDocument方法:

    public void startDocument ()
    {
        System.out.println("Start document");
    }

    public void endDocument ()
    {
        System.out.println("End document");
    }


start/endDocument事件处理器不接收任何参数。当SAX驱动找到文档开始的时候,它将调用startDocument方法一次;当它找到文档尾的时候,它将调用endDocument方法一次(即使有错误存在)。

上面的例子只是打印了一条消息到标准输出流中,但你的程序可以在这些事件处理器中包含任何代码:最通常的做法是,程序将在内存中构建某种类型的树,产生输出,操纵数据库,或从XML流中抽取信息。

SAX驱动也以同样的方式对元素开始和结束作出响应,所不同的是它将传入参数给方法startElement和endElement:

    public void startElement (String uri, String name,
                  String qName, Attributes atts)
    {
    if ("".equals (uri))
        System.out.println("Start element: " + qName);
    else
        System.out.println("Start element: {" + uri + "}" + name);
    }

    public void endElement (String uri, String name, String qName)
    {
    if ("".equals (uri))
        System.out.println("End element: " + qName);
    else
        System.out.println("End element:   {" + uri + "}" + name);
    }

这个方法在每一个元素开始或结束的时候打印一行消息,括号中的是名字空间URI。参数qName包含的是XML 1.0中定义的名字,只能用于所有没有名字空间URI的元素。在这个快速入门里,我们不会看到访问属性的代码;你可以通过属性名来访问它们,或者,如果它们构成一个矢量的话,你可以遍历它们。

最后,SAX2将通过characters方法报告字符数据;下面的实现将会把所有的字符数据打印到屏幕上;因为它需要对特殊字符做专门处理,所以会花费较长时间:

public void characters (char ch[], int start, int length)
    {
    System.out.print("Characters:    /"");
    for (int i = start; i < start + length; i++) {
        switch (ch[i]) {
        case '//':
        System.out.print("////");
        break;
        case '"':
        System.out.print("///"");
        break;
        case '/n':
        System.out.print("//n");
        break;
        case '/r':
        System.out.print("//r");
        break;
        case '/t':
        System.out.print("//t");
        break;
        default:
        System.out.print(ch[i]);
        break;
        }
    }
    System.out.print("/"/n");
    }


注意,SAX驱动会按照自己的方式随意组织字符数据成块,因此,你不能指望一个元素内容所有的字符数据都能被一个characters事件所捕获。

4 SAX2应用实例


以下是一个完整的例子程序(再强调一下,在真实的应用中,各事件处理器可能分别由不同的类实现):

import java.io.FileReader;

import org.xml.sax.XMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.DefaultHandler;


public class MySAXApp extends DefaultHandler
{

    public static void main (String args[])
    throws Exception
    {
    XMLReader xr = XMLReaderFactory.createXMLReader();
    MySAXApp handler = new MySAXApp();
    xr.setContentHandler(handler);
    xr.setErrorHandler(handler);

    // Parse each file provided on the command line.
    for (int i = 0; i < args.length; i++) {
        FileReader r = new FileReader(args[i]);
        xr.parse(new InputSource(r));
    }
    }


    public MySAXApp ()
    {
    super();
    }


    ////////////////////////////////////////////////////////////////////
    // Event handlers.
    ////////////////////////////////////////////////////////////////////


    public void startDocument ()
    {
    System.out.println("Start document");
    }


    public void endDocument ()
    {
    System.out.println("End document");
    }


    public void startElement (String uri, String name,
                  String qName, Attributes atts)
    {
    if ("".equals (uri))
        System.out.println("Start element: " + qName);
    else
        System.out.println("Start element: {" + uri + "}" + name);
    }


    public void endElement (String uri, String name, String qName)
    {
    if ("".equals (uri))
        System.out.println("End element: " + qName);
    else
        System.out.println("End element:   {" + uri + "}" + name);
    }


    public void characters (char ch[], int start, int length)
    {
    System.out.print("Characters:    /"");
    for (int i = start; i < start + length; i++) {
        switch (ch[i]) {
        case '//':
        System.out.print("////");
        break;
        case '"':
        System.out.print("///"");
        break;
        case '/n':
        System.out.print("//n");
        break;
        case '/r':
        System.out.print("//r");
        break;
        case '/t':
        System.out.print("//t");
        break;
        default:
        System.out.print(ch[i]);
        break;
        }
    }
    System.out.print("/"/n");
    }

}


5 例子输出


参考下面的XML文档:

<?xml version="1.0"?>

<poem xmlns="http://www.megginson.com/ns/exp/poetry">
<title>Roses are Red</title>
<l>Roses are red,</l>
<l>Violets are blue;</l>
<l>Sugar is sweet,</l>
<l>And I love you.</l>
</poem>


假设该文档名为roses.xml,名为com.example.xml.SAXDriver(实际上根本不存在这样的驱动)的SAX2驱动被部署在你的classpath中,你可以这样调用例子程序:

java -Dorg.xml.sax.driver=com.example.xml.SAXDriver MySAXApp roses.xml

运行后,得到如下结果:

Start document
Start element: {http://www.megginson.com/ns/exp/poetry}poem
Characters:    "/n"
Start element: {http://www.megginson.com/ns/exp/poetry}title
Characters:    "Roses are Red"
End element:   {http://www.megginson.com/ns/exp/poetry}title
Characters:    "/n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Roses are red,"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "/n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Violets are blue;"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "/n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "Sugar is sweet,"
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "/n"
Start element: {http://www.megginson.com/ns/exp/poetry}l
Characters:    "And I love you."
End element:   {http://www.megginson.com/ns/exp/poetry}l
Characters:    "/n"
End element:   {http://www.megginson.com/ns/exp/poetry}poem
End document


注意这个短文档所产生的(至少)25个事件:所用到的6个元素的开始和结束(活着,你愿意的话,一个开始标签,一个结束标签),11个字符数据块(包括元素间的空白),一个文档开始标签,一个文档结束标签。如果输入文档不包括xmlns="http://www.megginson.com/ns/exp/poetry"属性来声明所有元素都在此名称空间中,则输出结果会如下所示:

Start document
Start element: poem
Characters:    "/n"
Start element: title
Characters:    "Roses are Red"
End element:   title
Characters:    "/n"
Start element: l
Characters:    "Roses are red,"
End element:   l
Characters:    "/n"
Start element: l
Characters:    "Violets are blue;"
End element:   l
Characters:    "/n"
Start element: l
Characters:    "Sugar is sweet,"
End element:   l
Characters:    "/n"
Start element: l
Characters:    "And I love you."
End element:   l
Characters:    "/n"
End element:   poem
End document

你很可能两种类型的文档都会用到:有的使用XML名称空间,有的不使用。你处理的文档中也可能有的元素(属性)有名称空间,而有的却没有。要确定你的程序中有检测名称空间URI的代码,而不是假定程序处理的文档总有名称空间URI(或总是没有名称空间URI)。

根据SAX Quickstart整理,原文见:http://www.saxproject.org/quickstart.html
原创粉丝点击