SAX

一.什么是SAX?

  SAX(Sample API for XML),即XML简单API,它是由一组接口和类构成的,用于提供一种解析XML文档的方法。我们知道XML是用一种层次化的结构来存储数据的,解析的意思就是用某种方法来提取出其中的元素,属性和数据,以便用这些信息进行进一步的操作,比如用提取出的某些符合条件的信息与客户端交互。解析的方法除了SAX方法外,还有DOM(Document Object Model)。这两种方法差别很大:SAX是基于事件的方法,它很类似于标签库的处理机制,在标签开始,标签结束以及错误发生等等地方调用相应的接口实现方法。这就给我们提供了一个可以分析元素和数据的机会。SAX是顺序的,层次化的分析XML文档,着眼于当前的事件连续的处理,不是全部文档都读入内存,而DOM的做法正是将XML文档元素全部读入内存,生成一棵包含全部内容的树,以便全局的控制各个节点元素。解析一个XML文档需要借助XML解析器来完成,它将验证文档的结构是否良好。有些解析器还具有验证功能(带有或打开验证功能的解析器称为验证解析器或有效解析器;不带验证功能的解析器称为未验证解析器或无效解析器),可以进一步验证文档的有效性。比较常用的解析器有Apache Xerces,IBM XML4J,Sun ProjectX,Oracle XML Parser等等,基于性能和规范性的考虑,这里使用Apache Xerces。

二.常用SAX 2.0 API

org.xml.asx.Attrbutes 接口:用于得到属性的个数,名字和值。
org.xml.asx.ContentHandler 接口:定义了处理XML文档所能调用的事件方法。
org.xml.asx.DTDHandler 接口:定义了解析DTD时所能调用的事件方法。
org.xml.sax.EntityResolver 接口:用来处理调用外部实体事件。
org.xml.sax.ErrorHandler 接口:定义了三种级别的异常事件。
org.xml.sax.InputSource 类:用于封装压缩XML文档,供SAX解析器输入。
org.xml.sax.Locator 类:用于对解析过程进行定位,可以取得当前行数等信息。
org.xml.sax.SAXException 类:SAX的异常基础。
org.xml.sax.SAXNotRecongnizedException 类:发现不可识别的标示符异常。
org.xml.sax.SAXNotSupportedException 类:发现可识别但是不支持的标示符异常。
org.xml.sax.SAXParseException 类:解析过程中发生异常。
org.xml.sax.XMLFilter 接口:用来取得XMLReader自身信息。
org.xml.sax.XMLReader 类:用于解析XML文档。
org.xml.sax.helpers.XMLReaderAdapter 类:用SAX1.0的格式执行SAX2.0 XMLReader
org.xml.sax.helpers.XMLReaderFactory 类:动态创建XMLReader实例。

三.解析XML的例子

  这个例子演示了使用Apache Xerces解析器对存有学生资料的XML文档进行解析,并将解析后得到的数据显示出来。

---------- SutInfo.xml ----------

<?xml version="1.0"?>
<?xml-stylesheet href="xsl/StuInfo.xsl" type="text/xsl"?>
<!DOCTYPE LIT:StuInfo SYSTEM "dtd/student.dtd">

<LIT:StuInfo xmlns:LIT="http://www.lit.edu.cn/student/">
   
    <LIT:student>
        <LIT:name>bigmouse</LIT:name>
        <LIT:sex>male</LIT:sex>
        <LIT:lesson>
            <LIT:lessonName>math</LIT:lessonName>
            <LIT:lessonScore>60</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>Englist</LIT:lessonName>
            <LIT:lessonScore>59</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>autoCAD</LIT:lessonName>
            <LIT:lessonScore>80</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>SCM</LIT:lessonName>
            <LIT:lessonScore>90</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>mechanics</LIT:lessonName>
            <LIT:lessonScore>61</LIT:lessonScore>
        </LIT:lesson>
    </LIT:student>

    <LIT:breakLine/>

    <LIT:student>
        <LIT:name>coco</LIT:name>
        <LIT:sex>female</LIT:sex>
        <LIT:lesson>
            <LIT:lessonName>math</LIT:lessonName>
            <LIT:lessonScore>90</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>Englist</LIT:lessonName>
            <LIT:lessonScore>95</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>C++</LIT:lessonName>
            <LIT:lessonScore>80</LIT:lessonScore>
        </LIT:lesson>
        <LIT:lesson>
            <LIT:lessonName>Java</LIT:lessonName>
            <LIT:lessonScore>85</LIT:lessonScore>
        </LIT:lesson>
    </LIT:student>

    <LIT:breakLine/>

    <LIT:master>&masterName;</LIT:master>

</LIT:StuInfo>

---------- StuInfo.xsl ----------

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:LIT="http://www.lit.edu.cn/student/"
        version="1.0">

    <xsl:template match="LIT:StuInfo">
        <html>
            <head>
                <title>Student Information</title>
            </head>
            <body>
                <xsl:apply-templates select="*"/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="LIT:student">
        <li>Name:<xsl:value-of select="LIT:name"/></li>
        <li>Sex:<xsl:value-of select="LIT:sex"/></li>
        <xsl:for-each select="LIT:lesson">
            <li>Lesson:<xsl:value-of select="LIT:lessonName"/>(<xsl:value-of select="LIT:lessonScore"/>)</li>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="LIT:breakLine">
        <hr/>
    </xsl:template>

    <xsl:template match="master">
        <xsl:copy-of select="*"/>
    </xsl:template>

</xsl:stylesheet>

---------- student.dtd ----------

<!ELEMENT LIT:StuInfo ((LIT:student, LIT:breakLine)*, LIT:master)>
<!ATTLIST LIT:StuInfo xmlns:LIT CDATA #REQUIRED>
<!ELEMENT LIT:student (LIT:name, LIT:sex, LIT:lesson*)>
<!ELEMENT LIT:name (#PCDATA)>
<!ELEMENT LIT:sex (#PCDATA)>
<!ELEMENT LIT:lesson (LIT:lessonName, LIT:lessonScore)>
<!ELEMENT LIT:lessonName (#PCDATA)>
<!ELEMENT LIT:lessonScore (#PCDATA)>
<!ELEMENT LIT:breakLine EMPTY>
<!ELEMENT LIT:master (#PCDATA)>
<!ENTITY masterName SYSTEM "master.txt">

---------- MySAXParser.java ----------

import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class MySAXParser
{
  public MySAXParser()
  {
  }

  public static void main(String[] args)
  {
    if (args.length != 1)
    {
      System.out.println("Usage:java MySAXParser XMLFileURI");
      System.exit(0);
    }

    MySAXParser mySAXParser = new MySAXParser();
    mySAXParser.parserXMLFile(args[0]);
  }

  /**
   * 解析文档
   * @param fileURI XML文档的URI
   */
  private void parserXMLFile(String fileURI)
  {
    try
    {
      //通过指定解析器的名称来动态加载解析器
      XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

      //处理内容前要注册内容管理器
      parser.setContentHandler(new MyContentHandler());

      //处理错误前要注册错误管理器
      parser.setErrorHandler(new MyErrorHandler());

      //处理DTD前要注册DTD管理器
      parser.setDTDHandler(new MyDTDHandler());

      //打开解析器的验证
      parser.setFeature("http://xml.org/sax/features/validation", true);

      //开始解析文档
      parser.parse(fileURI);
    }
    catch (IOException ioe)
    {
      System.out.println(ioe.getMessage());
    }
    catch (SAXException saxe)
    {
      System.out.println(saxe.getMessage());
    }
  }
}

---------- MyContentHandle.java ----------

import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

public class MyContentHandler implements ContentHandler
{
  //DTD中定义的元素
  private static final String ELEMENT_NAME = "name";
  private static final String ELEMENT_SEX = "sex";
  private static final String ELEMENT_LESSON = "lesson";
  private static final String ELEMENT_LESSON_NAME = "lessonName";
  private static final String ELEMENT_LESSON_SCORE = "lessonScore";
  private static final String ELEMENT_STUDENT = "student";
  private static final String ELEMENT_LINE = "breakLine";

  private String currentData = "";  //当前元素的数据
  private String lessonName = "";
  private String lessonScore = "";

  public MyContentHandler()
  {
  }

  /**
   * 当其他某一个调用事件发生时,先调用此方法来在文档中定位。
   * @param locator
   */
  public void setDocumentLocator(Locator locator)
  {

  }

  /**
   * 在解析整个文档开始时调用
   * @throws SAXException
   */
  public void startDocument() throws SAXException
  {
    System.out.println("**** Student information start ****");
  }

  /**
   * 在解析整个文档结束时调用
   * @throws SAXException
   */
  public void endDocument() throws SAXException
  {
    System.out.println("**** Student information end ****");
  }

  /**
   * 在解析名字空间开始时调用
   * @param prefix
   * @param uri
   * @throws SAXException
   */
  public void startPrefixMapping(String prefix, String uri) throws SAXException
  {

  }

  /**
   * 在解析名字空间结束时调用
   * @param prefix
   * @throws SAXException
   */
  public void endPrefixMapping(String prefix) throws SAXException
  {

  }

  /**
   * 在解析元素开始时调用
   * @param namespaceURI
   * @param localName
   * @param qName
   * @param atts
   * @throws SAXException
   */
  public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException
  {

  }


/**
* 在解析元素结束时调用
* @param namespaceURI
* @param localName 本地名,如student
* @param qName 原始名,如LIT:student
* @throws SAXException
*/
public void endElement(String namespaceURI, String localName, String qName) throws SAXException
{
if (localName.equals(ELEMENT_NAME))
{
System.out.println(localName + ":" + currentData);
}

if (localName.equals(ELEMENT_SEX))
{
System.out.println(localName + ":" + currentData);
}

if (localName.equals(ELEMENT_LESSON_NAME))
{
this.lessonName = currentData;
}

if (localName.equals(ELEMENT_LESSON_SCORE))
{
this.lessonScore = currentData;
}

if (localName.equals(ELEMENT_LESSON))
{
System.out.println(lessonName + ":" + lessonScore);
}

if (localName.equals(ELEMENT_LINE))
{
System.out.println("------------------------------------");
}
}

/**
* 取得元素数据
* @param ch
* @param start
* @param length
* @throws SAXException
*/
public void characters(char[] ch, int start, int length) throws SAXException
{
currentData = new String(ch, start, length).trim();
}

/**
* 取得元素数据中的空白
* @param ch
* @param start
* @param length
* @throws SAXException
*/
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
{

}

/**
* 在解析到处理指令时,调用此方法。
* 这些处理指令不包括XML的版权指令,它由解析器本身识别。
* @param target
* @param data
* @throws SAXException
*/
public void processingInstruction(String target, String data) throws SAXException
{

}

/**
* 当未验证解析器忽略实体时调用此方法
* @param name
* @throws SAXException
*/
public void skippedEntity(String name) throws SAXException
{

}
}

---------- MyErrorHandler.java ----------

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ErrorHandler;
public class MyErrorHandler implements ErrorHandler
{

public MyErrorHandler()
{
}

/**
* XML的警告信息
* @param exception
* @throws SAXException
*/
public void warning(SAXParseException exception) throws SAXException
{
System.out.println("!!!WARNING!!!");
System.out.println(exception.getLineNumber() + ":(" + exception.getSystemId() + ")" + exception.getMessage());
}

/**
* 不符合XML规范时调用此方法
* @param exception
* @throws SAXException
*/
public void error(SAXParseException exception) throws SAXException
{
System.out.println("!!!ERROR!!!");
System.out.println(exception.getLineNumber() + ":(" + exception.getSystemId() + ")" + exception.getMessage());
}

/**
* 非良构的文档时调用此方法
* @param exception
* @throws SAXException
*/
public void fatalError(SAXParseException exception) throws SAXException
{
System.out.println("!!!FATAL!!!");
System.out.println(exception.getLineNumber() + ":(" + exception.getSystemId() + ")" + exception.getMessage());
}
}

---------- MyDTDHandler.java ----------

import org.xml.sax.SAXException;
import org.xml.sax.DTDHandler;

public class MyDTDHandler implements DTDHandler
{

public MyDTDHandler()
{
}

/**
* 当实体声明为不必解析的实体时调用此方法,比如NDATA类型。
* @param name
* @param publicId
* @param systemId
* @throws SAXException
*/
public void notationDecl(String name, String publicId, String systemId) throws SAXException
{
System.out.println("**notationDecl**");
System.out.println("name:" + name);
System.out.println("publicId" + publicId);
System.out.println("systemId:" + systemId);
}

/**
* 当处理符号声明时调用此方法,比如NOTATION。
* @param name
* @param publicId
* @param systemId
* @param notationName
* @throws SAXException
*/
public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException
{
System.out.println("**unparsedEntityDecl**");
System.out.println("name:" + name);
System.out.println("publicId" + publicId);
System.out.println("systemId:" + systemId);
System.out.println("notationName:" + notationName);
}
}


---------- 解析后得到结果 ----------

**** Student information start ****
name:bigmouse
sex:male
math:60
Englist:59
autoCAD:80
SCM:90
mechanics:61
------------------------------------
name:coco
sex:female
math:90
Englist:95
C++:80
Java:85
------------------------------------
**** Student information end ****


四.关于其他技术

上面介绍了SAX解析XML文档的方法,它不将整个文档放入内存,而是以基于事件的方式来处理文档,因此在速度和性能上优于DOM。但是在可读性上,SAX却不如DOM操作清楚简单。因此在文档不是特别大的时候,还是采用DOM方法比较合适。另外还有一种解析XML的API -- JDOM,它是一种基于Java2的完整API,同样具有SAX的高效,快速的特点,而且还可以像DOM那样从整体上操纵文档,提供一种比DOM更简单的生成和访问元素节点的方法。我将会在以后的文章中介绍DOM,JDOM以及JAXP等技术。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值