SAX 2.0 - Simple API for XML

来源:互联网 发布:app程序怎么编程 编辑:程序博客网 时间:2024/05/22 13:01
一.什么是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="xslStuInfo.xsl" type="text/xsl"?>
<!DOCTYPE LIT:StuInfo SYSTEM "dtdstudent.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等技术。