随笔 - 3, 文章 - 152, 评论 - 17, 引用 - 0
数据加载中……

SAX之Java实现学习笔记

本文假设读者对XML有些了解

 

首先,先给出一个比较基本的处理xml文件的程序。你不必细看,直接跳过即可。需要时可以返回来看。

 

 

Echo01.java

 

import java.io.*;

 

import org.xml.sax.*;

import org.xml.sax.helpers.DefaultHandler;

 

import javax.xml.parsers.SAXParserFactory;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

 

public class Echo01 extends DefaultHandler

{

    StringBuffer textBuffer;

 

    public static void main(String argv[])

    {

        if (argv.length != 1) {

            System.err.println("Usage: cmd filename");

            System.exit(1);

        }

 

        // Use an instance of ourselves as the SAX event handler

        DefaultHandler handler = new Echo01();

 

        // Use the default (non-validating) parser

              SAXParserFactory factory = SAXParserFactory.newInstance();

        try {

            // Set up output stream

            out = new OutputStreamWriter(System.out, "UTF-8");

 

            // Parse the input

            SAXParser saxParser = factory.newSAXParser();

            saxParser.parse( new File(argv[0]), handler);

 

        } catch (Throwable t) {

            t.printStackTrace();

        }

        System.exit(0);

    }

 

    static private Writer  out;

 

    //===========================================================

    // SAX DocumentHandler methods

    //===========================================================

 

    public void startDocument()

    throws SAXException

    {

        emit("<?xml version='1.0' encoding='UTF-8'?>");

        nl();

    }

 

    public void endDocument()

    throws SAXException

    {

        try {

            nl();

            out.flush();

        } catch (IOException e) {

            throw new SAXException("I/O error", e);

        }

    }

 

    public void startElement(String namespaceURI,

                             String sName, // simple name

                             String qName, // qualified name

                             Attributes attrs)

    throws SAXException

    {

        echoText();

              String eName = sName; // element name

        if ("".equals(eName)) eName = qName; // not namespaceAware

        emit("<"+eName);

        if (attrs != null) {

            for (int i = 0; i < attrs.getLength(); i++) {

                String aName = attrs.getLocalName(i); // Attr name

                if ("".equals(aName)) aName = attrs.getQName(i);

                emit(" ");

                emit(aName+"=\""+attrs.getValue(i)+"\"");

                          }

        }

        emit(">");

    }

 

    public void endElement(String namespaceURI,

                           String sName, // simple name

                           String qName  // qualified name

                          )

    throws SAXException

    {

        echoText();

        String eName = sName; // element name

        if ("".equals(eName)) eName = qName; // not namespaceAware

        emit("</"+eName+">");

    }

 

    public void characters(char buf[], int offset, int len)

    throws SAXException

    {

                     String s = new String(buf, offset, len);

        if (textBuffer == null) {

           textBuffer = new StringBuffer(s);

        } else {

           textBuffer.append(s);

        }

    }

 

    //===========================================================

    // Utility Methods ...

    //===========================================================

 

    // Display text accumulated in the character buffer

    private void echoText()

    throws SAXException

    {

        if (textBuffer == null) return;

                           String s = ""+textBuffer;

              emit(s);

              textBuffer = null;

    }

 

    // Wrap I/O exceptions in SAX exceptions, to

    // suit handler signature requirements

    private void emit(String s)

    throws SAXException

    {

        try {

            out.write(s);

            out.flush();

        } catch (IOException e) {

            throw new SAXException("I/O error", e);

        }

    }

 

    // Start a new line

    private void nl()

    throws SAXException

    {

      String lineEnd =  System.getProperty("line.separator");

        try {

            out.write(lineEnd);

        } catch (IOException e) {

            throw new SAXException("I/O error", e);

        }

    }

}

 

从程序中可以看出,解析一个XML文件的核心语句是下面一部分:

     // Use an instance of ourselves as the SAX event handler

        DefaultHandler handler = new Echo01();

 

        // Use the default (non-validating) parser

              SAXParserFactory factory = SAXParserFactory.newInstance();

        try {

            // Set up output stream

            out = new OutputStreamWriter(System.out, "UTF-8");

 

            // Parse the input

            SAXParser saxParser = factory.newSAXParser();

            saxParser.parse( new File(argv[0]), handler);

 

        } catch (Throwable t) {

            t.printStackTrace();

        }

先是创建一个SAXParserFactory工厂类的实例,然后通过SAXParser saxParser = factory.newSAXParser(); 这个工厂类的方法创建了一个saxParser。将xml文件(new File(argv[0]))和一个Sax Event Handlerhandler)(在这个程序里面,这个Handler其实是本身这个类,这个类继承了org.xml.sax.helpers.DefaultHandler 这个类,并且在前面初始化了它:DefaultHandler handler = new Echo01();  )传递给它,让它进行解析。

 

关于xml文件的解析过程中的处理全部在Handler里面实现。一般Parser接受的是DefaultHandler或者HandlerBase这两个类。 这个例子里面的类是继承DefaultHandler这个虚类的。看下图:

 

 

DefaultHandler是实现了EntityResolver, DTDHandler, ContentHandler, ErrorHandler四个接口的虚类。分别定义了如下的方法:

 

 

不同的方法,在不同的时候被Parser调用,(这个不同的时候就是Event-based

详细介绍:(暂略)

 

 

DefualtHandlerUML图如下:

 

 

看完Handler,再转过头去看Parser,在代码里面用的是SAXParser(SAXParser saxParser)

仔细看里面的代码

 

你会发现,其实它并没有自己完成解析的工作,而是Wrap了另二个类XMLReaderParser来完成解析工作。原来SAXParser只是起到一个Adapter的工作而已。

 

UML:

 

那我们去看看Parserorg.xml.sax.Parser)去,

 

看到Parser的代码,你会大失所望。原来Parser也只是一个空壳子而已

 

 

图中你可以看到经过层层查找的Parser只是一个接口而已。

 

回想一下前面看到生成解析器代码的时候

使用了工厂模式

              SAXParserFactory factory = SAXParserFactory.newInstance();

        try {

            // Set up output stream

            out = new OutputStreamWriter(System.out, "UTF-8");

 

            // Parse the input

            SAXParser saxParser = factory.newSAXParser();

            saxParser.parse( new File(argv[0]), handler);

完全由SAXParserFactory 这个类来控制产生的Parser的类型。我们只是拿来用就可以了。秘密一定藏在里面。

 

看到代码,发现原来这个工厂自己也是一个虚类,连返回的工厂的实例都是该虚工厂的一个实现而已。

再去看真正的实现org.apache.crimson.jaxp.SAXParserFactoryImpl

 

 

发现它又wrapSAXParserImpl,可知SAXParserImplSAXParser的一个子类。

继续追踪下去,因为SAXParserImpl继承了SAXParser,所以它也继承了SAXParser的方法。在SAXParserImpl体内,并没有发现覆写掉parser方法的地方,所以SAXParserImplparser也就是SAXParser的那个parser,呵呵,是不是有点绕口令的味道?那么怎么我们绕了半天,又回去了呢。再仔细看看SAXParserparser方法

 

 

 

可以看到其实在里面的Parser parser这个实例是调用了this.getParser()这个方法来得到的。再看看SAXParser里面的getParser方法

 

 

是不是有点感觉了? 对了,其实这个方法就是留给继承了SAXParserSAXParserImpl来实现的,这样,SAXParser的子类就可以自由的改换Parser。只要改写掉getParser方法就可以了。

急忙去看SAXParserImplgetParser这个方法

 

 

 

你会发现你又上当了,这里又给出了很暧昧的代码,并不是我们所猜想的那样是一个真正的实现,再仔细看看。 注释里面有这么句话:Adapt a SAX2 XMLReader into a SAX1 Parser

XMLReader,是不是很熟?想想看哪里看到过的?对了,刚刚在SAXParser体内Wrap的二个类,一个是我们追踪至今的Parser,另一个就是XMLReader,原来这个XMLReader才是才是现在在用的SAX2解析器,而为了保持对以前系统的兼容才保留了SAX1解析器Parser,但其实是通过对XMLReaderWrap得到的。只是个Adapte而已。

 

好了,现在可以集中火力去查找XMLReader了。有了刚刚的经验,很容易的,我们发现,和Parser一样,XMLReader也是一个接口:

 

 

很容易的,我们找到XMLReaderImpl

 

 

 

和里面的parse方法:

 

 

 

可以看到里面选择parse的部分,是根据是否需要Validation选择不同的parser的实现。

 

 

Parser的真面目已经找到啦。

Parser2

 

 

ValidatingParser

 

其实,ValidatingParser也是继承了Parser2的一个类而已,再加上了验证合法性的功能。

 

 

今天先找到这里吧。 SAX的实现包含了许多Pattern,这里只是冰山一角,慢慢回味。。。

posted on 2005-03-28 16:15 阅读(442) 评论(0)  编辑  收藏 所属分类: Java_Xml


只有注册用户登录后才能发表评论。


网站导航: