XML学习《SAX解析》

来源:互联网 发布:阿里云改系统 编辑:程序博客网 时间:2024/04/27 18:32

1.01SAX解析器及工作原理
    SAX(Simple API for XML)提供了解析XML文件的API,和DOM解析器比,不用将XML文件读入内存建立树状结
    构。SAX的 核心是事件处理机制,SAX解析器调用: parse(File f,DefaultHandler dh);
    解析XML文件,并向该方法传递一个事件处理器。SAX解析器在解析XML文件是过程中,根据从文件中解析出来
    的数据产生相应的事件,并报告这个事件给事件处理器,事件处理器就会处理所发现的数据,parse方法必须等
    待事件处理完毕后再继续解析文件,报告下一个事件,因此,对于SAX来说,较DOM解析器更高的效率,事件处
    理器每次在内存中只保留对一个事件的处理,处理完毕后即可释放该处理过程所占用的内存。
    使用SAX解析器的步骤如下:
    1:  SAXParserFactory factory= SAXParserFactory.newInstance();
    2:  SAXParser saxParser = factory.newSAXParser();
    同时要使用到javax.xml.parsers这个包。
    如果想要SAXParserFactory产生的SAX解析支持名称空间的话,
    可让factory对象调用setNamespaceAware(true)。

1.02:事件处理器
    SAX解析器使用下述方法解析XML文件:
    public void parse(File,DefaultHandler) throws SAXException,IOException;
    public void parse(InputStream,DefaultHandler) throws SAXException,IOException;
    public void parse(String,DefaultHandler) throws SAXException,IOException;

1.03:事件的产生与处理 
    SAX解析器的核心是事件处理机制,当SAX解析器调用parse()方法解析XML文件时,事件处理器会根据所产生的
    事件调用相应的方法来处理发现的数据,在编写程序时,需要使用DefaultHandler类的子类创建一个事件处理
    器,当处理器对报告的事件不感性趣时,就直接调用父类的方法,采用默认的处理方法,当处理器对报告的事件
    感性趣时,子类可以重写父 类的某些方法,调用重写的方法,以便处理器可以具体的处理解析器报告的数据。

1.04:文件的开始与结束
    当解析器开始解析XML文件时,就会报告"文件开始"事件,事件给事件处理器,然后再陆续地报告其他的事件,
    比如:"开始标记","文本事件",最后再报告"文件结束"事件。解析器报告"文件开始"事件,事件处理器就会调用:
    startDocument()方法,解析器报告"文件结束"事件,事件处理器会调用endDocument()方法,解析器在解析XML
    文件的过程中,只能报告一次"文件开始"和"文件结束"事件,换句话说,也只会调用一次startDocument()和
    endDocument()方法各一次。

1.05:XML中的处理指令
    XML中的处理指令比较特殊,它们不属性标记,它们为XML文件规定了特殊的规则,例如:
    <?xml version="1.0" encoding="UTF-8"?>
    处理指令规定XML文件必须使和UTF-8编码来保存和解析,解析器不报告XML声明给事件处理器,即不会报告上述
    指令。 但如下XML指令会报告给事件处理器:<?xml-stylesheet href="" type=""?>
    当解析器开始解析XML文件时,如果发现处理指令就会报告一个"处理指令"事件给事件处理器,事件处理器就会
    调用 void processingInstruction(String target,Strin data)方法,该方法中的参数target和data就是解析发现的数
    据。
    例如:对于<?xml-stylesheet href="" type=""?>处理指令,
    target就是"xml-stylesheet",data就是"href="" type="""

1.06开始标记与结束标记
    当解析器发现一个标记开始时,就将所发现的数据封装为一个"开始标记"事件,并报告该事件处理器,事件处理
    器就会知道发生的事件,然后调用:
    startElement(String uri,String localName,String qName,Attributes atts);
    方法对发现的数据处理,方法中的参数atts是解析器发现的标记的全部属性,当SAXParserFactory支持设置名称
    空间时,例如:factory.setNamespaceAware(true);
    参数uri的取值就是解析器发现的标记的名称空间,如果没有名称空间,uri是空字符组成的串,uri="", 
    localName是标记的名称,qName是带名称空间前缀的标记名称(如果有名称空间的前缀)或标记的名称(如果没有
    名称空间的前缀)。
    当SAXParserFactory对象没有设置支持名称空间时,相当于,factory.setNamespaceAware(false);
    那么uri和localName的值是空字符串,qName是带名称空间前缀的标记名称(如果有名称空间的前缀)或标记名称
    (如果没有名称空间的前缀)。
    事件处理器调用完startElement方法后,将陆续地收到解析器报告的其他事件,最后一定会报告该标记的
    "标记结束"事件,这时事件处理器会调用:
    endElement(String uri,String localName,String qName,Attributes atts);
    方法对发现的数据做出处理。
    如果一个标记是空标记,解析器也报告"标记开始"和"标记结束"事件。

1.07:文本数据
    XML文件中的标记的内容可以有文本数据,当解析器解析这些数据时,就报告"文本数据"事件给处理器,事件处
    理器就会知道发生的事件,然后调用:
    public void characters(char[] int start,int length);
    方法对解析的数据做出处理,参数字符数组ch中存放的就是解析的文本数据,start是数组ch中存放字符的起始位
    置,length是存放字符的个数。标记之间的缩格都是为了使XML文件看起来美观而形成的,但解析器不知道这一
    点,所以解析也会认为他们是有用的文本数据,当解析器解析这样的数据时,也会报告一个"文本事件"给事件处
    理器。注意:对于文本数据,解析器可能分成几个连续的"文本数据"报告给事件处理器。
 
1.08:处理空白
    标记之间的缩格都是为了使XML文件看起来美观而形成的,但解析器不知道这一点,所以解析也会认为他们是有
    用的文本数据,当解析器解析这样的数据时,也会报告一个"文本事件"给事件处理器。
    这些文本可能由空白类字符组成。
    例如:
    <?xml version="1.0"?>
    <root>
        <a>hello</a>
        <b>你好</b> 
    </root>
    SAX解析器并不知道标记之间的缩进区域是为了使得XML文件看起来更美观,对于上述XML文件,解析器一共报
    告了5次"文本数据"事件给事件处理器。显然,我们可能不希望事件处理器去调用characters方法来处理标记之间
    的缩 进区域所形成的空白字符,因为这样会延时事件处理器获取其他数据的时间。如果不想让事件处理 器去调
    用characters方法来处理的话,那么XML必须有效的,而且所关联的DTD文件必须规定XML文件的标记不能有混
    内容, 一旦这样做,当解析器报告的"文本数据"事件属于标记之间的缩进区域所形成的空白类字符时,事件处理
    器就会去调用:
    ignorableWhitespace(char[] ch,int start,int length); 方法,而不会调用character()方法。
    如果我们不准备处理这些空白,只要在编写DefaultHandler为的子类时直接从父类继承该方法即可,如果准备处
    这些空白类字符,只要在子类重写ignorableWhitespace方法即可。

1.09:名称空间
    名称空间的目的是有效的区分名字相同的标记,当二个标记的名字相同时,他们可能通过属于不同的名称空间来
     区分。名称空间通过使用声明名称空间来建立,分为有前缀和无前缀的名称空间。 
    名称空间声明是在一个标记的开始标记中,当解析器在一个标记的开始标记中发现一个名称空间时,就先报告一
    个 "名称空间开始"的事件给事件处理器,然后再报告"开始标记"事件。事件处理器会调用:
    startPrefixMapping(String prefix,String uri)
    其中:方法中的参数prefix是解析器发现的名称空间的前缀,uri是名称空间的名称,如果名称空间没有前缀,prefix
    是不含任何字符的空串,即prefix=""。名称空间涉及到作用域的概念,一个标记如果使用了名称空间,那么该名
    称空间的作用域是该标记及其子标记,因此,当解析报告完"结束标记"事件后,就会报告一个"名称空间结束"事件
    给事件处理器,表明名称空间的作用域结束,事件处理器就会知道发生的事件,然后调后:
    endPrefixMapping(String prefix,String uri)
    例如:<hello xmlns:java="eee">你好</hello>
    解析器报告事件的顺序如下:
    "名称空间开始"-->"开始标记"-->"文本数据"-->"结束标记"-->"名称空间结束"
    为了让解析器报告"名称空间"事件,SAXParser_Factory需要调用setNamespaceAware(true)。
 
1.10:处理错误
    SAX解析器默认地检查XML是否规范的,如果让SAX解析器检查XML文件是否有效,SAXParserFactory对象
    factory事件 必须进行如下设置:
    factory.setValidating(true);SAX解析器在解析XML文件的过程中,如果发现错误就会报告一 个"错误"事件给解析,
    报告的信息是一个SAXParserExcepiton对象,事件处理就会调用下列某个方法来处理信息:
    public void warning(SAXException e) throws SAXException;
    public void error(SAXException e)    throws SAXException;
    public void fatalError(SAXException e) throws SAXException;

1.11不可解析实体 
    实体分为可以被解析的实体和不可以被解析的实体,可解析实体就是能被解析器解析的实体。
    例如:你好,&lt;中国&gt;
    不可解析实体就是解析器无法解析的数据,通常指二进制数据,XML文件中不可以引用不可解析实体。
    例如:你好,&中国
    外部实体和内部实体有很大的不同,我们引用的外部实体可以是文本数据或是二进制数据,而且编码也可能和我
    们的XML不同。
    例如:   一个外部实体内容为:你you
    并没有包括任何特殊字符,但该文件的保存存编码可能是ANSI或其他编码,但不是UTF-8,ANSI使用2个字节编
    码"你",而UTF-8编码使用3个编码"你",那么解析器将文件的实际内容和引用该文件的XML放在一起做解析时,  
   就会发现UTF-8不支持 编码值"你",从而导致错误。
 
1.12关于SAXException异常
    在使用SAX处理解析过程中出现的错误,当发生致命的错误时,应用抛出一个SAXException对象给解析器,解析
    器将停止parse方法的执行。
    实际上,DefaultHandler类中的方法都可以抛出一个SAXException对象给解析器,比如,事件处理器在调用
    startDocument()方法时,突然决定终止解析文件,就可以抛出一个SAXException对象给解析器,解析器停止
    parse方法的 执行,不再报告任何事件给事件处理器。

一个完整的例子
example.xml:

<?xml version="1.0" ?>
<guest>
    <spent name="weng">1213</spent> 
    <spent name="li">3213</spent>
    <spent name="rui">213</spent>
    <spent name="weng">6213</spent>
    <spent name="weng">2088</spent>
    <spent name="zhang">1013</spent>
</guest>

<!--========SaxXml.java============== -->

import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SaxOne extends DefaultHandler {
    public static int count = 0 ;
 
    public static void main(String[] args){
               File file = new File("I://JAVA//Eclipse//XmlDemo//ch5-3.xml");  
               SAXParserFactory saxFactory = SAXParserFactory.newInstance();
               try {
                     SAXParser parser = saxFactory.newSAXParser();    
                     parser.parse(file,new SaxOne());    
               } catch (ParserConfigurationException e) {
                     e.printStackTrace();
               } catch (SAXException e) {
                     e.printStackTrace();
               } catch (IOException e){
                     e.printStackTrace();
               }
               System.out.println("/n事件处理器处理了"+ count +"个事件");
    }
 
    public void startDocument()  throws SAXException{
               System.out.println("开始解析XML文件");
               count ++;
    }
    public void startElement(String uri,String localName,String qName,Attributes attributes)
    throws SAXException{
               System.out.print("<"+qName+">");
               count++;
    }
    public void endDocument() throws SAXException{
               System.out.println();
               System.out.println("结束解析XML文件");
               count ++;
    }
    public void endElement(String uri, String localName, String qName) throws SAXException{
               System.out.print("</" + qName + ">");
               count ++;
    }
    public void characters(char[] ch,int start,int length) throws SAXException{
               System.out.print(new String(ch,start,length));
               count ++;  
    }
    public void processingInstruction(String target, String data) throws SAXException{
               System.out.println("处理指令的目标是" + target);
               System.out.println("处理指令的内容是" + data);
    }
    public void setDocumentLocator(Locator locator){
               System.out.println(locator.getColumnNumber() + ":" + locator.getLineNumber());
    }
}

 

原创粉丝点击