XML第十六讲:SAX方式解析XML文档深入详解 续

  上一讲我们讲解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对应的树形结构在内存中都帮我们构造好对象了,我们只要根据这种结构去写代码就可以了。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值