




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
SAX 编程技术1、概述SAX API(Simple API for XML),它是由一组接口和类构成的。在运行中的各方面表现都优于DOM API,但SAX用起来不像DOM那样直观。SAX它并不是由W3C官方所提出的标准,可以说是民间的事实标准。实际上,它是一种社区性质的讨论产物。虽然如此,在XML中对SAX的应用丝毫不比DOM少,几乎所有的XML解析器都会支持它。SAX解释器,可以说是一个把具体操作留给编程人员而把解释工作留给自己的一个编程模型。它并没有象DOM那样把整个XML文档加载到内存而是逐行解释然后通过事件通知给程序,由具体的程序使用这些事件通知,最后加以处理。2、SAX和DOM API(1)SAX的实现机制SAX API是基于事件驱动的(而DOM是文档驱动),它很类似于标签库的处理机制,在标签开始,标签结束以及错误发生等等地方调用相应的接口实现方法。与DOM比较而言,SAX是一种轻量型的方法。实现了SAX API的XML解析器跟据解析到的XML文档的不同特征产生事件。通过在Java代码中捕捉这些事件,就可以写出由XML数据驱动的程序(有些类似于AWT中的事件驱动机制,通过事件驱动来识别XML文档的内容)。由于SAX API是基于事件驱动的,SAX是顺序的,层次化的分析XML文档,着眼于当前的事件连续的处理,不是全部文档都读入内存,因此它并不需要读入整个文档。而文档的读入过程也就是SAX的解析过程(读入文档的过程和解析的过程是同时进行的),这和DOM区别很大。解析开始之前,需要向XMLReader注册一个ContentHandler,也就是相当于一个事件监听器,在ContentHandler中定义了很多方法,比如startDocument(),它定制了当在解析过程中,遇到文档开始时应该处理的事件。当XMLReader读到合适的内容,就会抛出相应的事件,并把这个事件的处理权代理给ContentHandler,调用其相应的方法进行响应。(2)SAX解析过程的说明:以下面的XML文档为例 Ogden Nash Fleas Adam l 当XMLReader读到标签时,就会调用ContentHandler.startElement()方法,并把标签名POEM作为参数传递过去。在你实现的startElement()方法中需要做相应的动作,以处理当出现时应该做的事情各个事件随着解析的过程(也就是文档读入的过程)一个个顺序的被抛出,相应的方法也会被顺序的调用,最后,当解析完成,方法都被调用后,对文档的处理也就完成了。l 下面的这个表,列出了在解析上面的那个XML文件的时候,顺序被调用的方法:遇到的项目方法回调文档开始startDocument() startElement(null,POEM,null,Attributes) characters( ., 6, 1) startElement(null,AUTHOR,null,Attributes) Ogden Nashcharacters( ., 15, 10) endElement(null,AUTHOR,null) characters( ., 34, 1)startElement(null,TITLE,null,Attributes)Fleascharacters( ., 42, 5)endElement(null,TITLE,null) characters( ., 55, 1)startElement(null,LINE,null,Attributes)Adamcharacters( ., 62, 4) endElement(null,LINE,null) characters( ., 67, 1)endElement(null,POEM,null)文档结束endDocument() (3)SAX的不足要注意的是SAX中一个重要的特点就是它的流式处理,在遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。(4)DOM的不足而DOM API是一种基于对象的API。实现DOM的XML解析器在内存中生成代表XML文档内容的一般对象模型。XML解析器一旦完成解析,内存中也就有了一个同时包含XML文档的结构和内容信息的DOM对象树。DOM的不足:DOM在用于对性能要求高的程序设计时却存在严重的不足。目前支持DOM的XML解析器都使用一种对象存储的方式,也就是创建很多小的代表DOM节点的对象,这些节点对象还包含了文本或嵌套其它DOM节点。这看似顺理成章,但却造成了性能的下降。Java中最为影响性能的操作之一是new操作符。对应于new操作符的每一次执行,在对所得对象的所有引用都消失后,垃圾收集器都要负责将这个对象从内存中清除。DOM API的众多小对象一般在解析完后被立即抛弃,这几乎耗光了JVM的所有内存。DOM的另一个不足是它把整个的XML文档都装入内存。对于大的文档,这就成了一个问题。再一次地,因为DOM是基于许多小对象实现的,所以在保存XML文档的同时,JVM还要用额外的几字节来保存关于所有这些对象的信息,这样一来,内存使用就变得比XML文档本身还要大。 DOM API的另一个不易察觉的问题是,使用它写成的代码要扫描XML文档两次。第一次将DOM结构读进内存,第二次定位感兴趣的数据。理所当然,定位不同的数据块就要在DOM结构中来回地移动。相反,SAX编程模式支持一趟同时定位和收集XML数据。(5)求肋于SAX 相对DOM API,SAX API是一种颇有吸引力的解决方案。SAX没有一般的对象模型,所以在内存消耗和性能问题上对new操作符的滥用没有顾忌。同时如果你要设计自己特定问题域的对象模型,SAX也就没有冗余的对象模型。并且,SAX一遍就能处理好XML文档,它所需的处理时间大大减少。可以适合比较大的XML文件的处理工作。DOM编程相对简单,但是速度比较慢,占用内存多,而SAX编程复杂一些,但是速度快,占用内存少。所以,我们应该根据不同的环境选择使用不同的方法。大部分的XML应用基本都可以用它们来解决。需要特别说明的是,DOM和SAX其实都是语言无关的,并非Java所独有,也就是说,只要有相应的语言实现,DOM和SAX可以应用在任何面向对象的语言中。3、在API中关于SAX的主要的几个包: org.xml.sax、org.xml.sax.helpers、org.xml.sax.ext。常用SAX2.0API如下: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.0XMLReaderorg.xml.sax.helpers.XMLReaderFactory类:动态创建XMLReader实例。4、编程SAX(1)编程SAX的方法使用SAX API的主要任务就是创建一个实现ContentHandler接口的类,一个供XML 解析器调用以将分析XML文档时所发生的SAX事件分发给处理程序的回调接口。方便起见,SAX API也提供了一个已经实现了ContentHandler接口的DefaultHandler适配器类。一但实现了ContentHandler或者扩展了DefaultHandler类,你只需直接将XML解析器解析一个特定的文档即可。(2)SAX编程的要点l SAX是一套API,不是一个解析器l 对于SAX而言事件的抛出是由解释器完成的,而处理则是交给应用程序完成的,所以实现事件接口和填写事件代码就是我们的事情。l 对于事件可以处理也可以不处理,不过由于SAX是不创建任何对象的,所以事件的状态也是不保持的。l 如果想要在多个事件中维护状态,也同样是应用程序的事情。(3)SAX的开发步骤l 实现ContentHandler 接口并填写事件代码(也可以继承DefaultHandler类,因为该类实现了ContentHandler 接口)l 创建SAX解释器工厂对象l 通过工厂对象创建SAX解释器l 使用SAX解释器加载XML文档,把已经实现了ContentHandler 接口的类的实例对象装入到解释器中l 解释器会回调程序中的事件过程(4)代码示例:请见SAXCounter.java程序,本程序主要实现对某一XML文档中的各个标签进行统计出现的次数import org.xml.sax.helpers.DefaultHandler;import javax.xml.parsers.*;import org.xml.sax.*;import org.xml.sax.helpers.*;import java.util.*;import java.io.*;public class SAXCounter extends DefaultHandler/(1)实现ContentHandler 接口并填写事件代码private Hashtable tags; /这个Hashtable用来记录tag出现的次数public static void main(String args) String xmlFilename = SAXCounter.xml;SAXParserFactory spf = SAXParserFactory.newInstance(); /(2)创建SAX解释器工厂对象SAXParser saxParser=null;try saxParser = spf.newSAXParser();/ (3)通过工厂对象创建SAX解释器SAXParser对象catch (Exception ex) System.err.println(ex);System.exit(1);try / (4)使用SAX解释器加载XML文档,把已经实现了ContentHandler 接口的类实例对象装入到解释器中saxParser.parse(new File(xmlFilename),new SAXCounter();catch (SAXException se) System.err.println(se.getMessage();System.exit(1);catch (IOException ioe) System.err.println(ioe);System.exit(1);/ (5)解释器会回调程序中的事件过程,本方法可以实现处理文档前的工作public void startDocument() throws SAXException tags = new Hashtable();/初始化Hashtable/对每一个开始元属进行处理public void startElement(String namespaceURI, String localName,String qName, Attributes atts)throws SAXExceptionString key = qName;/获得标签的名称字符串Object value = tags.get(key);/识别在Hastable中是否有该同名的标签存在if (value = null) / 如果是新碰到的标签,这在Hastable中添加一条该新的标签记录tags.put(key, new Integer(1);else / 如果以前碰到过,得到其计数值,并加1int count = (Integer)value).intValue();count+;tags.put(key, new Integer(count);/解析完成后的统计工作public void endDocument() throws SAXException Enumeration e = tags.keys();while (e.hasMoreElements() String tag = (String)e.nextElement();int count = (Integer)tags.get(tag).intValue();System.out.println(Tag occurs + count + times);执行结果如下:5、DefaultHandler适配器类在DefaultHandler适配器类的子类中,主要重载三个方法:l startElement(String tag, AttributeList attrs)l characters(char ch, int start, int length)l endElement(String name)(1)startElement()方法l 它在读取一行XML数据的开始标记时被触发l 子类覆盖这个方法后就可以在处理XML节点前先进行自己的处理逻辑。lll 该事件告诉我们标签元素的名称、该元素所有属性的名称和值,还会告诉名称空间的信息。public void startElement(String uri,String localName,String qualifiedName, Attributes attributes) throws SAXException 在SAX1.0版本中并不支持命名空间,而在新的2.0版本中提供了对命名空间的支持,这儿参数中的uri就是命名空间,localName是标签名,qName是标签的修饰前缀,当没有使用命名空间的时候,这两个参数都为null。l uri 命名空间的URI, 如果该标记没有命名空间,可以为空字符串l localName 标签名,没有prefix的XML节点名字(一个不包括名称空间的元素名称)l qualifiedName标签的修饰前缀,带有prefix的XML节点的完整名字(即名称空间前缀和元素本地名称的组合),应该采用该qualifiedName来识别标签的名称。l atts 代表该节点标记的属性列表(通过atts可以得到所有的属性名和相应的值。因为该对象提供了几种方法获取属性的名称和值,以及该元素的属性个数),如果该节点标记没有属性,将为null。l 这个方法抛出一个org.xml.sax.SAXException异常。 (2)characters()方法它主要用来处理标签之间(标签体)的具体数据,也即在处理节点数据的时候被触发。可以覆盖这个方法来进行数据操作的处理。如果您的 XML 应用程序需要存储特定元素的内容,可以把存储那些内容的代码放在 characters() 事件处理程序中。 public void characters (char ch, int start, int length) throws SAXException 其中参数ch代表一个字符数组,start代表字符数组的开始位置,length代表要取的字符数组中ch中的元素个数,同样的这个方法抛出一个org.xml.sax.SAXException异常。 我们可以很容易的用String类的一个构造方法来获得这个字符串的String类对象。String charEncontered=new String(ch,start,length)。(3)endElement()方法l 它在处理节点元素结束标记的时候被触发,也就是碰到结束标记的时候。l 可以覆盖这个方法来进行数据的收尾工作,比如将节点数据写入到文件中。public void endElement(String namespaceURI,String localName,String qName) throws SAXException 其中参数的说明请见startElement()方法,这个方法同样也抛出一个org.xml.sax.SAXException异常。(4)startDocument()和endDocument()方法void startDocument():当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。void endDocument():和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。6、SAX的应用示例应用示例1(1)示例中用到的XML文档样本XML轻松学习手册张三20040620(2)XML解析的代码:在main()方法中,主要做的就是创建解析器,然后解析文档。实际上,在这儿创建SAXParser对象的时候,为了使程序代码于具体的解析器无关,使用了同DOM中一样的设计技巧:通过一个SAXParserFactory类来创建具体的SAXParser对象,这样,当需要使用不同的解析器的时候,要改变的,只是一个环境变量的值,而程序的代码可以保持不变。这就是FactoryMethod模式的思想。/本程序主要是显示说明各个事件的触发过程import org.xml.sax.*;import org.xml.sax.helpers.*;import java.io.*;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;public class SAXExample extends DefaultHandler / 重载DefaultHandler类的方法以拦截SAX事件通知。 public SAXExample() SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser saxParser=null; try saxParser = spf.newSAXParser();/ Create a JAXP SAXParser saxParser.parse(new File(XMLDOMData.xml),this); catch (SAXException se) System.out.println(se.getMessage(); catch (IOException ioe) System.out.println(ioe); catch (Exception ex) System.out.println(ex); public void startDocument( ) throws SAXException System.out.println( SAX Event: START DOCUMENT(文档开始) );public void endDocument( ) throws SAXException System.out.println( SAX Event: END DOCUMENT(文档结束) );public void startElement( String namespaceURI,String localName,String qName,Attributes attr ) throws SAXException /打印除该标签的名称 System.out.println( SAX Event: START ELEMENT + qName + ); for ( int i = 0; i attr.getLength(); i+ ) / 如果有属性,我们也一并打印出来 System.out.print( ATTRIBUTE: +attr.getLocalName(i) + VALUE: +attr.getValue(i) +t); public void endElement( String namespaceURI,String localName,String qName ) throws SAXException System.out.println( SAX Event: END ELEMENT + qName + );public void characters( char ch, int start, int length ) throws SAXException System.out.print( SAX Event: CHARACTERS 开始); try OutputStreamWriter outw = new OutputStreamWriter(System.out); outw.write( ch, start,length );/将该标签体的文字串输出到控制台上 outw.flush(); catch (Exception e) e.printStackTrace(); System.out.print( SAX Event: CHARACTERS 结束);public static void main( String argv )SAXExample obj=new SAXExample(); 应用示例2(1)示例中用到的XML文档样本 编程珠玑 Jon Bentley7-5083-1914-128.0 Java编程思想(第2版)Bruce Eckel7-111-10441-299.0 Inside VCL(深入核心VCL架构剖析)李维7-5053-9489-480.0 (2)XML解析的代码:/本程序是获得XML文件中的各个标签的属性和标签体的文字串内容import java.io.File;import javax.xml.parsers.*;import org.xml.sax.helpers.*;import org.xml.sax.*;public class ReadXMLBySax extends DefaultHandlerprivate String ElementName;private int id;private String bookName;private String bookAuthor;private String bookISBN;private String bookPrice;public ReadXMLBySax()this.ElementName=;this.id=0;this.bookName=;this.bookAuthor=;this.bookISBN=;this.bookPrice=;public void startDocument()System.out.println(开始读XML文档);public void startElement(String uri,String localName,String qName,Attributes attributes)this.ElementName=qName;if(qName.equals(Book)/识别是否是指定的标签System.out.println(id属性:+attributes.getValue(0);/打印该名称的标签的属性的值public void characters(char ch,int start,int length)String tagBodyText=new String(ch,start,length);/获得标签体的文字串内容/*下列的代码主要是识别是否是指定的名称的标签,如果是并且识别其标签体是否为空,最后获得标签体的文字串*/if(this.ElementName.equals(bookName) & !tagBodyText.trim().equals()this.bookName=tagBodyText;if(this.ElementName.equals(bookAuthor) & !tagBodyText.trim().equals()this.bookAuthor=tagBodyText;if(this.ElementName.equals(bookISBN) & !tagBodyText.trim().equals()this.bookISBN=tagBodyText;if(this.ElementName.equals(bookPrice) & !tagBodyText.trim().equals()this.bookPrice=tagBodyText; public void endElement(String uri,String localName,String qName)/*下列的代码主要是打印出对应的标签体的文字串*/if(qName.equals(bookName)System.out.println(bookName:+this.bookName);if(qName.equals(bookAuthor)System.out.println(bookAuthor:+this.bookAuthor);if(qName.equals(bookISBN)System.out.println(bookISBN:+this.bookISBN);if(qName.equals(bookPrice)System.out.println(bookPrice:+this.bookPrice);if(qName.equals(Book)System.out.println(); this.ElementName=;public void endDocument()System.out.println(XML文档结束);public static void main(String args) throws ExceptionSAXParserFactory factory=SAXParserFactory.newInstance();SAXParser parser=factory.newSAXParser();parser.parse(new File(BookXml.xml),new ReadXMLBySax();(3)执行后的结果应用示例3/本程序是采用Properties来记录XML的各个标签和名称和值import org.xml.sax.Attributes;import org.xml.sax.helpers.DefaultHandler;import org.xml.sax.SAXException; import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import java.util.Properties;import java.io.File;/使用DefaultHandler的好处 是 不必实现出所有的方法,public class GetXMLTagNameAndValue extends DefaultHandler private Properties props;/定义一个Properties 用来存放各个标签的值private String currentSet;private String currentName;private StringBuffer currentValue = new StringBuffer();public GetXMLTagNameAndValue() ps = new Properties();/初始化propspublic Properties getProps() return ps;/定义开始解析元素的方法. 这里是将中的名称xxx提取出来.public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException /每次获得某一标签的内容时,先清空字符缓冲currentValue.delete(0, currentValue.length();this.currentName =qName;/获得该标签的名称/这里是将之间的标签体的值加入到currentValuepublic void characters(char ch, int start, int length) throws SAXException currentValue.append(ch, start, length);/在遇到结束后,将之前的名称和值一一对应保存在props中(页可以采用Hashtable来记录)public void endElement(String uri, String localName, String qName) throws SAXException props.put(qName.toLowerCase(), currentValue.toString().trim();public static void main(String args)Properties xmlProps=null;SAXParser saxParser;GetXMLTagNameAndValue saxEventCallBack = new GetXMLTagNameAndValue();SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();/获取SAX工厂对象saxParserFactory.setNamespaceAware(false);saxParserFactory.setValidating(false);trysaxParser = saxParserFactory.newSAXParser();/创建出SAX解析/将解析器和解析对象XMLDOMData.xml联系起来,同时指定事件回调方法的对象开始解析 saxParser.parse(new File(XMLDOMData.xml), saxEventCallBack); /获取解析成功后的属性,以后 我们其他应用程序只要调用本程序的props就可以提取出属性名称和值了xmlProps = saxEventCallBack.getProps();catch (javax.xml.parsers.ParserConfigurationException pe) System.out.println(pe.getMessage(); catch (SAXException se) System.out.println(se.getMessage(); catch (java.io.IOException ioe) System.out.println(ioe); catch (Exception ex) System.out.println(ex); finallysaxParserFactory=null;saxParser=null;saxEventCallBack=null;System.out.println(获得的各个标签的内容如下:);System.out.println(+xmlProps.getProperty(bookTitle)+);System.out.println(+xmlProps.getProperty(author)+);System.out.println(+xmlProps.getProperty(email)+);System.out.println(+xmlProps.getProperty(date)+);/*对带有属性的标签如何获得其属性值,比较困难。需要编程startElement方法中的Attributes参数*/7、利用栈来实现对文档结构的纪录(1)问题对SAX编程来说,当遇到一个开始标签的时候,在startElement()方法中,我们并不能够得到这个标签在XML文档中所处的位置。这在处理XML文档的时候是个大麻烦,因为在XML中标签的语义,有一部分是由其所处的位置所决定的。而且在一些需要验证文档结构的程序中,这更是一个问题。(2)解决的方法可以使用一个栈来实现对文档结构的纪录,栈的特点是先进先出。在startElemnt()方法中用push将这个标签的名字添加到栈中,在endElement()方法中在把它pop出来。我们知道对一个结构良好的XML而言,其嵌套结构是完备的,每一个开始标签总会对应一个结束标签,而且不会出现标签嵌套之间的错位。因而,每一次startElement()方法的调用,必然会对应一个endElement()方法的调用,这样push和pop也是成对出现的,我们只需要分析栈的结构,就可以很容易的知道当前标签所处在文档结构中的位置了。(3)java.util.Stack 类及编程思路public class SAXReader extends DefaultHandler Jav
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 济宁市2024-2025学年八年级上学期语文期中测试试卷
- 高速公路档案培训课件
- 高血压因素课件
- 高能相机基础知识培训课件
- 建设工程压覆矿产资源评估服务合同
- QMS考试试题及答案
- 电网知识新员工培训课件
- 【Nox聚星】2025年欧洲网红营销生态报告
- 高考加油课件app
- 电瓶车充电安全知识培训课件
- 上海牛津英语9A教案
- 绿色施工及环境保护施工方案
- 人教部编版道德与法治九年级下册教材解读及单元目标
- 外请手术医师知情同意书
- 财务尽职调查工作方案
- 焊接和切割作业的防火、防爆措施
- 公路工程质量监督综合检查内容
- 人事任命书红头文件模板
- 纽扣参考资料专用英语名词08.4
- 《导游英语》全套课件(完整版)
- SAE_USCAR2_Rev3_2001 汽车电子连接器性能标准
评论
0/150
提交评论