SAX解析XML 详解

来源:互联网 发布:服装淘宝店铺介绍 编辑:程序博客网 时间:2024/06/05 04:09

JAVA 解析 XML 通常有两种方式,DOM 和 SAX。DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一个驻留内存的树结构(节点树),然后您的代码才可以使用 DOM 的标准接口来操作这个树结构。但大部分情况下我们只对文档的部分内容感兴趣,根本就不用先解析整个文档,并且从节点树的根节点来索引一些我们需要的数据也是非常耗时的。 
    SAX是一种XML解析的替代方法。相比于文档对象模型DOM,SAX 是读取和操作 XML 数据的更快速、更轻量的方
法。SAX 允许您在读取文档时处理它,从而不必等待整个文档被存储之后才采取操作。它不涉及 DOM 所必需的开销和概念跳跃。 SAX API是一个基于事件的API ,适用于处理数据流,即随着数据的流动而依次处理数据。SAX API 
在其解析您的文档时发生一定事件的时候会通知您。在您对其响应时,您不作保存的数据将会 被抛弃。
    下面是一个SAX解析XML的示例(有点长,因为详细注解了SAX事件处理的所有方法),SAX API中主要有四种处理事件的接口,它们分别是ContentHandlerDTDHandler, EntityResolver 和 ErrorHandler 。下面的例子可能有点冗长,实际上只要继承DefaultHandler 类 ,再覆盖一部分 处理事件的方法 同样可以达到这个示例的效果,但为了纵观全局,还是看看SAX API里面所有主要的事件解析方法吧。( 实际上DefaultHandler就是实现了上面的四个事件处理器接口,然后提供了每个抽象方法的默认实现。) 

1,ContentHandler 接口 :接收文档逻辑内容的通知 的处理器接口。

Java代码  收藏代码
  1. import org.xml.sax.Attributes;  
  2. import org.xml.sax.ContentHandler;  
  3. import org.xml.sax.Locator;  
  4. import org.xml.sax.SAXException;  
  5.   
  6. class MyContentHandler implements ContentHandler{  
  7.     StringBuffer jsonStringBuffer ;  
  8.     int frontBlankCount = 0;  
  9.     public MyContentHandler(){  
  10.         jsonStringBuffer = new StringBuffer();  
  11.     }  
  12.     /* 
  13.      * 接收字符数据的通知。 
  14.      * 在DOM中 ch[begin:end] 相当于Text节点的节点值(nodeValue) 
  15.      */  
  16.     @Override  
  17.     public void characters(char[] ch, int begin, int length) throws SAXException {  
  18.         StringBuffer buffer = new StringBuffer();  
  19.         for(int i = begin ; i < begin+length ; i++){  
  20.             switch(ch[i]){  
  21.                 case '\\':buffer.append("\\\\");break;  
  22.                 case '\r':buffer.append("\\r");break;  
  23.                 case '\n':buffer.append("\\n");break;  
  24.                 case '\t':buffer.append("\\t");break;  
  25.                 case '\"':buffer.append("\\\"");break;  
  26.                 default : buffer.append(ch[i]);   
  27.             }  
  28.         }  
  29.         System.out.println(this.toBlankString(this.frontBlankCount)+  
  30.                 ">>> characters("+length+"): "+buffer.toString());  
  31.     }  
  32.   
  33.       
  34.     /* 
  35.      * 接收文档的结尾的通知。 
  36.      */  
  37.     @Override  
  38.     public void endDocument() throws SAXException {  
  39.         System.out.println(this.toBlankString(--this.frontBlankCount)+  
  40.                 ">>> end document");  
  41.     }  
  42.   
  43.       
  44.     /* 
  45.      * 接收文档的结尾的通知。 
  46.      * 参数意义如下: 
  47.      *    uri :元素的命名空间 
  48.      *    localName :元素的本地名称(不带前缀) 
  49.      *    qName :元素的限定名(带前缀) 
  50.      *  
  51.      */  
  52.     @Override  
  53.     public void endElement(String uri,String localName,String qName)  
  54.             throws SAXException {  
  55.         System.out.println(this.toBlankString(--this.frontBlankCount)+  
  56.                 ">>> end element : "+qName+"("+uri+")");  
  57.     }  
  58.   
  59.     /* 
  60.      * 结束前缀 URI 范围的映射。 
  61.      */  
  62.     @Override  
  63.     public void endPrefixMapping(String prefix) throws SAXException {  
  64.         System.out.println(this.toBlankString(--this.frontBlankCount)+  
  65.                 ">>> end prefix_mapping : "+prefix);  
  66.     }  
  67.   
  68.     /* 
  69.      * 接收元素内容中可忽略的空白的通知。 
  70.      * 参数意义如下: 
  71.      *     ch : 来自 XML 文档的字符 
  72.      *     start : 数组中的开始位置 
  73.      *     length : 从数组中读取的字符的个数 
  74.      */  
  75.     @Override  
  76.     public void ignorableWhitespace(char[] ch, int begin, int length)  
  77.             throws SAXException {  
  78.         StringBuffer buffer = new StringBuffer();  
  79.         for(int i = begin ; i < begin+length ; i++){  
  80.             switch(ch[i]){  
  81.                 case '\\':buffer.append("\\\\");break;  
  82.                 case '\r':buffer.append("\\r");break;  
  83.                 case '\n':buffer.append("\\n");break;  
  84.                 case '\t':buffer.append("\\t");break;  
  85.                 case '\"':buffer.append("\\\"");break;  
  86.                 default : buffer.append(ch[i]);   
  87.             }  
  88.         }  
  89.         System.out.println(this.toBlankString(this.frontBlankCount)+">>> ignorable whitespace("+length+"): "+buffer.toString());  
  90.     }  
  91.       
  92.     /* 
  93.      * 接收处理指令的通知。 
  94.      * 参数意义如下: 
  95.      *     target : 处理指令目标 
  96.      *     data : 处理指令数据,如果未提供,则为 null。 
  97.      */  
  98.     @Override  
  99.     public void processingInstruction(String target,String data)  
  100.             throws SAXException {  
  101.         System.out.println(this.toBlankString(this.frontBlankCount)+">>> process instruction : (target = \""  
  102.                 +target+"\",data = \""+data+"\")");  
  103.     }  
  104.   
  105.     /* 
  106.      * 接收用来查找 SAX 文档事件起源的对象。 
  107.      * 参数意义如下: 
  108.      *     locator : 可以返回任何 SAX 文档事件位置的对象 
  109.      */  
  110.     @Override  
  111.     public void setDocumentLocator(Locator locator) {  
  112.         System.out.println(this.toBlankString(this.frontBlankCount)+  
  113.                 ">>> set document_locator : (lineNumber = "+locator.getLineNumber()  
  114.                 +",columnNumber = "+locator.getColumnNumber()  
  115.                 +",systemId = "+locator.getSystemId()  
  116.                 +",publicId = "+locator.getPublicId()+")");  
  117.           
  118.     }  
  119.   
  120.     /* 
  121.      * 接收跳过的实体的通知。 
  122.      * 参数意义如下:  
  123.      *     name : 所跳过的实体的名称。如果它是参数实体,则名称将以 '%' 开头, 
  124.      *            如果它是外部 DTD 子集,则将是字符串 "[dtd]" 
  125.      */  
  126.     @Override  
  127.     public void skippedEntity(String name) throws SAXException {  
  128.         System.out.println(this.toBlankString(this.frontBlankCount)+  
  129.                 ">>> skipped_entity : "+name);  
  130.     }  
  131.   
  132.     /* 
  133.      * 接收文档的开始的通知。 
  134.      */  
  135.     @Override  
  136.     public void startDocument() throws SAXException {  
  137.         System.out.println(this.toBlankString(this.frontBlankCount++)+  
  138.                 ">>> start document ");  
  139.     }  
  140.   
  141.     /* 
  142.      * 接收元素开始的通知。 
  143.      * 参数意义如下: 
  144.      *    uri :元素的命名空间 
  145.      *    localName :元素的本地名称(不带前缀) 
  146.      *    qName :元素的限定名(带前缀) 
  147.      *    atts :元素的属性集合 
  148.      */  
  149.     @Override  
  150.     public void startElement(String uri, String localName, String qName,   
  151.             Attributes atts) throws SAXException {  
  152.         System.out.println(this.toBlankString(this.frontBlankCount++)+  
  153.                 ">>> start element : "+qName+"("+uri+")");  
  154.     }  
  155.       
  156.     /* 
  157.      * 开始前缀 URI 名称空间范围映射。 
  158.      * 此事件的信息对于常规的命名空间处理并非必需: 
  159.      * 当 http://xml.org/sax/features/namespaces 功能为 true(默认)时, 
  160.      * SAX XML 读取器将自动替换元素和属性名称的前缀。 
  161.      * 参数意义如下: 
  162.      *    prefix :前缀 
  163.      *    uri :命名空间 
  164.      */  
  165.     @Override  
  166.     public void startPrefixMapping(String prefix,String uri)  
  167.             throws SAXException {  
  168.         System.out.println(this.toBlankString(this.frontBlankCount++)+  
  169.                 ">>> start prefix_mapping : xmlns:"+prefix+" = "  
  170.                 +"\""+uri+"\"");  
  171.           
  172.     }  
  173.       
  174.     private String toBlankString(int count){  
  175.         StringBuffer buffer = new StringBuffer();  
  176.         for(int i = 0;i<count;i++)  
  177.             buffer.append("    ");  
  178.         return buffer.toString();  
  179.     }  
  180.       
  181. }  

 

2,DTDHandler 接口 :接收与 DTD 相关的事件的通知的处理器接口。

Java代码  收藏代码
  1. import org.xml.sax.DTDHandler;  
  2. import org.xml.sax.SAXException;  
  3.   
  4. public class MyDTDHandler implements DTDHandler {  
  5.   
  6.     /* 
  7.      * 接收注释声明事件的通知。 
  8.      * 参数意义如下: 
  9.      *     name - 注释名称。 
  10.      *     publicId - 注释的公共标识符,如果未提供,则为 null。 
  11.      *     systemId - 注释的系统标识符,如果未提供,则为 null。 
  12.      */  
  13.     @Override  
  14.     public void notationDecl(String name, String publicId, String systemId)  
  15.             throws SAXException {  
  16.         System.out.println(">>> notation declare : (name = "+name  
  17.                 +",systemId = "+publicId  
  18.                 +",publicId = "+systemId+")");  
  19.     }  
  20.   
  21.     /* 
  22.      * 接收未解析的实体声明事件的通知。 
  23.      * 参数意义如下: 
  24.      *     name - 未解析的实体的名称。 
  25.      *     publicId - 实体的公共标识符,如果未提供,则为 null。 
  26.      *     systemId - 实体的系统标识符。 
  27.      *     notationName - 相关注释的名称。 
  28.      */  
  29.     @Override  
  30.     public void unparsedEntityDecl(String name,  
  31.             String publicId,  
  32.             String systemId,  
  33.             String notationName) throws SAXException {  
  34.         System.out.println(">>> unparsed entity declare : (name = "+name  
  35.                 +",systemId = "+publicId  
  36.                 +",publicId = "+systemId  
  37.                 +",notationName = "+notationName+")");  
  38.     }  
  39.   
  40. }  

 


3,EntityResolver 接口 :是用于解析实体的基本接口。

Java代码  收藏代码
  1. import java.io.IOException;  
  2.   
  3. import org.xml.sax.EntityResolver;  
  4. import org.xml.sax.InputSource;  
  5. import org.xml.sax.SAXException;  
  6.   
  7. public class MyEntityResolver implements EntityResolver {  
  8.   
  9.     /* 
  10.      * 允许应用程序解析外部实体。 
  11.      * 解析器将在打开任何外部实体(顶级文档实体除外)前调用此方法 
  12.      * 参数意义如下: 
  13.      *     publicId : 被引用的外部实体的公共标识符,如果未提供,则为 null。 
  14.      *     systemId : 被引用的外部实体的系统标识符。 
  15.      * 返回: 
  16.      *     一个描述新输入源的 InputSource 对象,或者返回 null, 
  17.      *     以请求解析器打开到系统标识符的常规 URI 连接。 
  18.      */  
  19.     @Override  
  20.     public InputSource resolveEntity(String publicId, String systemId)  
  21.             throws SAXException, IOException {  
  22.         return null;  
  23.     }  
  24.   
  25. }  

 
4,ErrorHandler接口 :是错误处理程序的基本接口。

Java代码  收藏代码
  1. import org.xml.sax.ErrorHandler;  
  2. import org.xml.sax.SAXException;  
  3. import org.xml.sax.SAXParseException;  
  4.   
  5. public class MyErrorHandler implements ErrorHandler {  
  6.   
  7.     /* 
  8.      * 接收可恢复的错误的通知 
  9.      */  
  10.     @Override  
  11.     public void error(SAXParseException e) throws SAXException {  
  12.         System.err.println("Error ("+e.getLineNumber()+","  
  13.                 +e.getColumnNumber()+") : "+e.getMessage());  
  14.     }  
  15.       
  16.     /* 
  17.      * 接收不可恢复的错误的通知。 
  18.      */  
  19.     @Override  
  20.     public void fatalError(SAXParseException e) throws SAXException {  
  21.         System.err.println("FatalError ("+e.getLineNumber()+","  
  22.                 +e.getColumnNumber()+") : "+e.getMessage());  
  23.     }  
  24.   
  25.     /* 
  26.      * 接收不可恢复的错误的通知。 
  27.      */  
  28.     @Override  
  29.     public void warning(SAXParseException e) throws SAXException {  
  30.         System.err.println("Warning ("+e.getLineNumber()+","  
  31.                 +e.getColumnNumber()+") : "+e.getMessage());  
  32.     }  
  33.   
  34. }  

 

Test 类的主方法打印解析books.xml时的事件信息。

Java代码  收藏代码
  1. import java.io.FileNotFoundException;  
  2. import java.io.FileReader;  
  3. import java.io.IOException;  
  4.   
  5. import org.xml.sax.ContentHandler;  
  6. import org.xml.sax.DTDHandler;  
  7. import org.xml.sax.EntityResolver;  
  8. import org.xml.sax.ErrorHandler;  
  9. import org.xml.sax.InputSource;  
  10. import org.xml.sax.SAXException;  
  11. import org.xml.sax.XMLReader;  
  12. import org.xml.sax.helpers.XMLReaderFactory;  
  13.   
  14.   
  15. public class Test {  
  16.   
  17.     public static void main(String[] args) throws SAXException,   
  18.             FileNotFoundException, IOException {  
  19.         //创建处理文档内容相关事件的处理器  
  20.         ContentHandler contentHandler = new MyContentHandler();  
  21.         //创建处理错误事件处理器  
  22.         ErrorHandler errorHandler = new MyErrorHandler();  
  23.         //创建处理DTD相关事件的处理器  
  24.         DTDHandler dtdHandler = new MyDTDHandler();  
  25.         //创建实体解析器  
  26.         EntityResolver entityResolver = new MyEntityResolver();  
  27.           
  28.         //创建一个XML解析器(通过SAX方式读取解析XML)  
  29.         XMLReader reader = XMLReaderFactory.createXMLReader();   
  30.         /* 
  31.          * 设置解析器的相关特性 
  32.          *     http://xml.org/sax/features/validation = true 表示开启验证特性 
  33.          *     http://xml.org/sax/features/namespaces = true 表示开启命名空间特性 
  34.          */  
  35.         reader.setFeature("http://xml.org/sax/features/validation",true);  
  36.         reader.setFeature("http://xml.org/sax/features/namespaces",true);  
  37.         //设置XML解析器的处理文档内容相关事件的处理器  
  38.         reader.setContentHandler(contentHandler);  
  39.         //设置XML解析器的处理错误事件处理器  
  40.         reader.setErrorHandler(errorHandler);  
  41.         //设置XML解析器的处理DTD相关事件的处理器  
  42.         reader.setDTDHandler(dtdHandler);  
  43.         //设置XML解析器的实体解析器  
  44.         reader.setEntityResolver(entityResolver);  
  45.         //解析books.xml文档  
  46.         reader.parse(new InputSource(new FileReader("books.xml")));  
  47.     }  
  48.   
  49. }  

 


books.xml 文件的内容如下:

 

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="GB2312"?>  
  2. <books  count="3" xmlns="http://test.org/books">  
  3.     <!--books's comment-->  
  4.     <book id="1">  
  5.         <name>Thinking in JAVA</name>  
  6.     </book>  
  7.     <book id="2">  
  8.         <name>Core JAVA2</name>  
  9.     </book>  
  10.     <book id="3">  
  11.         <name>C++ primer</name>  
  12.     </book>  
  13. </books>  

 
控制台输出如下:

 

>>> set document_locator : (lineNumber = 1,columnNumber = 1,systemId = null,publicId = null)
>>> start document 
Error (2,7) : Document is invalid: no grammar found.
Error (2,7) : Document root element "books", must match DOCTYPE root "null".

    >>> start prefix_mapping : xmlns: = "
http://test.org/books"
        >>> start element : books(
http://test.org/books)
            >>> characters(2): \n\t
            >>> characters(2): \n\t
            >>> start element : book(
http://test.org/books)
                >>> characters(3): \n\t\t
                >>> start element : name(
http://test.org/books)
                    >>> characters(16): Thinking in JAVA
                >>> end element : name(
http://test.org/books)
                >>> characters(2): \n\t
            >>> end element : book(
http://test.org/books)
            >>> characters(2): \n\t
            >>> start element : book(
http://test.org/books)
                >>> characters(3): \n\t\t
                >>> start element : name(
http://test.org/books)
                    >>> characters(10): Core JAVA2
                >>> end element : name(
http://test.org/books)
                >>> characters(2): \n\t
            >>> end element : book(
http://test.org/books)
            >>> characters(2): \n\t
            >>> start element : book(
http://test.org/books)
                >>> characters(3): \n\t\t
                >>> start element : name(
http://test.org/books)
                    >>> characters(10): C++ primer
                >>> end element : name(
http://test.org/books)
                >>> characters(2): \n\t
            >>> end element : book(
http://test.org/books)
            >>> characters(1): \n
        >>> end element : books(
http://test.org/books)
    >>> end prefix_mapping : 
>>> end document

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 处理违章分不够怎么办 收入证明不够两倍怎么办 驾驶证被扣留了怎么办 驾照没考过怎么办 驾驶证超过60天怎么办 扣分超过36分怎么办 护士资格证过期了怎么办 辞职护士执业证怎么办 网约护士怎么办手续 动车票待核验怎么办 店员不维护老板怎么办 准考证号会过期怎么办 驾校准考证丢了怎么办 科目二下大雨怎么办 考科目二下雨天怎么办 普通话总是二乙怎么办 科目二很紧张怎么办 18年科目四缺考怎么办 个人医保卡欠费怎么办 医保欠费不想交怎么办 怀化市驾考绿色通道怎么办? 签注易不能办理怎么办 网上怎么办护照和签证 意大利被偷护照怎么办 户口在学校 怎么办签证 户口换了身份证怎么办 广州在校大学生怎么办护照 民间借贷无法还怎么办 退伍档案没接收怎么办 档案被单位扣住怎么办 公积金提不出来怎么办 公积金还贷款怎么办手续 科一预约失败怎么办 科四忘记预约怎么办 我科目一缺考了怎么办? 无可选考试场地怎么办 早产儿脑部发育不好怎么办 宝宝脑部发育不好怎么办 小孩脑部发育不好怎么办 8岁儿童智力低下怎么办 儿童食物不耐受怎么办