在上篇博客中,我们介绍了两种访问XML的方式,一种是基于DOM文档的,另一种是基于SAX事件流的。在介绍面向事件访问方式的两种方法:SAX和STAX,提到了两个特别有意思的词:“推”模型和“拉”模型。
今天,我们就通过代码来看一下,它们是如何体现推和拉的。
【生活中的推拉模型】
在查找资料的时候,看到有人对这个推拉的比喻特别恰当。其实编程里面的一些词,都可以从现实生活中找到例子。例如这个推拉,就比如说发传单。发传单有两种方式:塞和取。前者是你经过的时候,发传单的人主动的扔给你,塞到你面前,而后者则向超市里的货架一样,摆在那里,等着你自己去取。
【编程中的推拉模型】
在我们的编程中的推拉,其实和现实生活中的推拉模型是一样的。它只不过是SAX方式解析XML的两种实现方法。其实,在我们的编程过程中也会遇到需要对某个对象进行监听,并在其发生变化的时候做出不同的反应情况。为实现此需求,我们有两种解决办法。
一、通过回调(CallBack)实现
定义一个函数,并且在其中编写变化处理语句,然后将其指针传递给被监听的对象,在被监听对象发生变化后,对其进行调用。这就是我们以事件为驱动的推模型(Event-driven programming)。就是对象发生改变的时候,用编写好的函数去处理这些参数。
二、通过轮询(Loop check)实现
不断去获取被监听对象的状态,在其改变后执行相应的语句。这就是拉模型,你获取被监听对象的状态,然后做出反应。
【SAX解析XML的推拉模型】
拉模型——STAX:获取XMLStreamReader的事件类型,然后执行相应的输出语句。
/** * 读取Xml */ public static void readDemo() throws FileNotFoundException,XMLStreamException { XMLInputFactory factory = XMLInputFactory.newInstance(); InputStream input=new FileInputStream(new File("test.xml")); XMLStreamReader r = factory.createXMLStreamReader(input); try { int event = r.getEventType(); while (true) { switch (event){ case XMLStreamConstants.START_DOCUMENT: //文档开始 System.out.println("Start Document."); break; case XMLStreamConstants.START_ELEMENT: //节点开始 System.out.println("Start Element: " + r.getName()); for(int i = 0,n = r.getAttributeCount(); i < n; ++i) System.out.println("Attribute: " + r.getAttributeName(i) + "=" + r.getAttributeValue(i)); //获取节点属性值 break; case XMLStreamConstants.CHARACTERS: //节点元素值 if (r.isWhiteSpace()) break; System.out.println("Text: " + r.getText()); break; case XMLStreamConstants.END_ELEMENT: System.out.println("End Element:" + r.getName()); //节点结束 break; case XMLStreamConstants.END_DOCUMENT: System.out.println("End Document."); //文档结束 break; } if (!r.hasNext()) break; event = r.next(); } } finally { r.close(); } }
注:int event=r.getEventType();这行代码就是获取XMLStreamReader的事件类型,如果是文档开始,输出什么,如果是节点开始输出什么,如果是节点元素值,又输出什么,它是根据监听对象的变化,而执行不同的输出语句。
public class test { public static void main(String[] args) throws SAXException,IOException { /*domDemo dd=new domDemo(); String str = "D:/grade.xml"; dd.init(); //dd.createXML(str); dd.praseXML(str);*/ //创建处理文档内容相关事件的处理器 ContentHandler contentHandler = new MyContentHandler(); //创建处理错误事件处理器 ErrorHandler errorHandler = new MyErrorHandler(); //创建处理DTD相关事件的处理器 DTDHandler dtdHandler = new MyDTDHandler(); //创建实体解析器 EntityResolver entityResolver = new MyEntityResolver(); //创建一个XML解析器(通过SAX方式读取解析XML) XMLReader reader = XMLReaderFactory.createXMLReader(); /* * 设置解析器的相关特性 * http://xml.org/sax/features/validation = true 表示开启验证特性 * http://xml.org/sax/features/namespaces = true 表示开启命名空间特性 */ reader.setFeature("http://xml.org/sax/features/validation",true); reader.setFeature("http://xml.org/sax/features/namespaces",true); //设置XML解析器的处理文档内容相关事件的处理器 reader.setContentHandler(contentHandler); //设置XML解析器的处理错误事件处理器 reader.setErrorHandler(errorHandler); //设置XML解析器的处理DTD相关事件的处理器 reader.setDTDHandler(dtdHandler); //设置XML解析器的实体解析器 reader.setEntityResolver(entityResolver); //解析test.xml文档 reader.parse(new InputSource(new FileReader("test.xml"))); }
注:在SAX中,当contentHandler,errorHandler,dtdHandler,entityResolver对象发生变化时,会调用XMLReader实例对象reader中写好的set方法。上面这几个对象就是我们上篇博客里讲到的SAX的常用事件处理器。