上一讲我们讲解XML解析中第二种解析XML的方式SAX解析,讲了基于SAX解析的过程: SAX方式解析XML文档深入详解
,现在我们练习一下。
1. 练习SAX解析XML,主要是明白一下SAX解析中的事件触发机制。
有一个XML文档,如下所示
<?xml version="1.0" encoding="UTF-8"?> <学生名册> <!-- This is my comment --> <学生 学号="1"> <姓名>张三</姓名> <性别>男</性别> <年龄>20</年龄> </学生> <学生 学号="2"> <姓名>李四</姓名> <性别>女</性别> <年龄>19</年龄> </学生> <学生 学号="3"> <姓名>王五</姓名> <性别>男</性别> <年龄>21</年龄> </学生> </学生名册>
使用SAX解析上面的XML文档,主要是观察一下在解析过程中基于事件的回调机制。
package com.ahuier.xml.sax; import java.io.File; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SaxTest1 { public static void main(String[] args) throws Exception{ //step1: 获得SAX解析器工厂实例 SAXParserFactory factory = SAXParserFactory.newInstance(); //step2:获得SAX解析器实例 SAXParser parser = factory.newSAXParser(); /* * step3: 开始进行解析 * 第一个参数是传递 XML的file对象,第二个参数是继承了DefaultHandler类的实例 * DefaultHandler 它里面封装了各个事件处理器的回调方法,显然我们必须去继承这个DefaultHandler,然后自己去实现它里面的方法。 */ parser.parse(new File("student.xml"), new MyHandler()); } } class MyHandler extends DefaultHandler{ @Override public void startDocument() throws SAXException { System.out.println("parse began"); } @Override public void endDocument() throws SAXException { System.out.println("parse end"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("start element"); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("end element"); } }
[编译执行结果,这里不再贴出来,主要是让大家了解以下SAX中遇到元素开始标签,结束标签等一些时间触发机制是如何。可以执行以上代码,根据执行结果结合XML文档自行分析以下。]
2. 使用SAX来解析上述XML文档中实际的内容,然后将其内容输出来。
package com.ahuier.xml.sax; import java.io.File; import java.util.Stack; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SaxTest2 { public static void main(String[] args) throws Exception { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); parser.parse(new File("student.xml"), new MyHandler2()); } } /* * 观察需要解析的XML文档,可以发现在进行解析的时候:比如 <姓名>张三</姓名> * 它有元素,内容,元素结束等,但是随着XML的解析,它只有startElement()这个方法,那它到底是 哪个 element 的开始呢? * 所以我们再解析的过程中是无法事先进行预知的,我们只是知道元素开始了,但是并不知道元素是什么! * 所以我们只有通过 startElement()方法中的参数来获知当前元素的一些信息,比如元素名字,属性之类的。 * 就好比如GUI中当我们事件触发的时候会有一个event对象,这个对象封装了当前事件产生的时候的一些相关信息, * 我们可以通过这些对象拿到当前事件的一些相关信息,比如按钮文本,按钮所在坐标等。 */ class MyHandler2 extends DefaultHandler{ /* * 使用stack数据结构,它是后进先出的特点 */ private Stack<String> stack = new Stack<String>(); private String name; private String gender; private String age; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //qName:表示元素的名字,标签的名字。 stack.push(qName); //将元素名字压到栈顶 //处理元素属性 for(int i = 0; i < attributes.getLength(); i++){ String attrName = attributes.getQName(i); String attrValue = attributes.getValue(i); System.out.println(attrName + "=" + attrValue); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { //peek()把栈中的值拿出来,当不弹出来,只取出值而已 //pop()把栈中的值弹出来 String tag = stack.peek(); //在输出元素内容之前,我们必须知道当前元素是哪一个元素,所以我们要进行判断 if("姓名".equals(tag)){ //提取元素内容 name = new String(ch, start, length); } else if("性别".equals(tag)){ gender = new String(ch, start, length); } else if("年龄".equals(tag)){ age = new String(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //遇到元素结尾,表示该元素已经解析完毕,需要从栈中弹出 stack.pop(); if("学生".equals(qName)){ System.out.println("学生: " + name); System.out.println("性别: " + gender); System.out.println("年龄: " + age); System.out.println(); } } }
[编译执行结果]:学号=1
学生: 张三
性别: 男
年龄: 20
学号=2
学生: 李四
性别: 女
年龄: 19
学号=3
学生: 王五
性别: 男
年龄: 21
[说明]:1): 使用SAX进行解析,不像使用DOM解析那样可以加载整个XML树,然后来随机访问。但是SAX再解析的过程中是无法事先进行预知是从哪个Element开始的,我们只是知道元素开始了,但是并不知道元素是什么!所以在SAX进行解析的时候,必须先把解析出来的东西保存起来,最好将其保存在集合中(我们这边是用栈(stack),它的实现可以用LinkedList实现),这样可以取出对其进行操作了,不然对一个节点解析过了,就无法返回继续解析了。
2): 现在我们已经基本完成了SAX解析XML的例子,从功能上说DOM能完成的SAX也能完成,只不过才去的策略是不一样的。在进行SAX解析的时候我们主要重写了DefaultHandler的几种解析XML方法,而不像DOM那样,DOM是比较直接的,它本身就是与XML对应的树形结构在内存中都帮我们构造好对象了,我们只要根据这种结构去写代码就可以了。