在上篇博客中,我们介绍了两种访问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的事件类型,如果是文档开始,输出什么,如果是节点开始输出什么,如果是节点元素值,又输出什么,它是根据监听对象的变化,而执行不同的输出语句。
推模型——SAX:对象发生变化时,调用编写好的处理函数
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的常用事件处理器。
【总结】