现在的位置: 首页 > 综合 > 正文

android中使用SAX解析xml文件

2014年09月05日 ⁄ 综合 ⁄ 共 5995字 ⁄ 字号 评论关闭

在正式的开始讲解SAX解析xml文件之前,先来看看xml文件的一基本的知识,语法我就不讲了,网上一搜一大堆,我用下面的这段代码作为示例

第一行没什么好说的,从第二行开始,图中标有红色的称为元素;黑色的称为元素的属性(Attributes)和属性值;蓝色称为文本节点(TextNode)。下面的李四跟张三是一样的,就不说了。
其中蓝色的地方除了张三和80以外,其他的地方什么也没有,但是他仍然是TextNode,实际上在这几个地方有两个字符\n\t,就是换行符和退格符。
我们都知道使用SAX是事件驱动的,什么叫事件驱动呢?简单的说就是遇到什么东西就做什么事,打个比方,你在路上开车,遇到红灯就踩刹车,遇到转弯就打方向盘。关于事件驱动这一点,我会在下面的例子中具体说明。

先来看下面两段代码:

package com.example.xml_parser;

import java.io.InputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
	public String string;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		TextView text = (TextView)findViewById(R.id.textView);
		try {
			string = this.getxml();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		text.setText(string);
	}
	
	protected String getxml() throws Exception{
		/* 1、买生产材料(使用SAX操作xml文件的之前,必须先要将xml文件转换成流对象)*/
		InputStream inputstream = this.getClass().getClassLoader()
				.getResourceAsStream("student.xml");
		/* 2、买生产机器(实例化一个DefaultHandler对象)*/
		SAXForHandler dh = new SAXForHandler();
		/* 3、创建一个解析工厂(实例化一个SAXParserFactory对象)*/
		SAXParserFactory spf = SAXParserFactory.newInstance();
		/* 4、招聘工人(实例化一个SAXParser对象)*/
		SAXParser parser = spf.newSAXParser();
		/* 5、工人开始工作,使用生产机器来加工材料*/
		parser.parse(inputstream, dh);
		/* 6、将生产好的产品(也就是解析完之后的xml文件)从机器中拿出来*/
		return dh.getString().toString();
	}
}
/* 这段代码中的几个方法的参数具体是表示什么呢?在这里先不用管,我在后面会继续说明,现在只需要弄清楚什么是事件驱动就行了*/
package com.example.xml_parser;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;

public class SAXForHandler extends DefaultHandler {

	private static final String TAG = "SAX_Parser";
	private String perTag; //这个变量用来记录当前正在读取到的标签
	StringBuffer strbuf;//这个字符串用来保存读取到的内容
	
	public StringBuffer getString(){
		return strbuf;
	}

        /* 开始读取文档的时候会触发这个方法,一般在这个方法进行一些初始化的处理*/
	public void startDocument() throws SAXException {
		Log.i(TAG, "startDocumnet()");
	}

        /* 读到xml文件中的开始元素时会触发这个方法*/
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		Log.i(TAG, "startElement()");
	}

	@Override
        /* 读取到文档中的文本节点(TextNode)时会触发这个方法*/
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		Log.i(TAG, "characters()");
	}

	@Override
        /* 读到xml文件中的结束元素时,会触发这个方法*
	public void endElement(String uri, String localName, String qName)
			throws SAXException { 
		Log.i(TAG, "endElement()");
	}

	@Override
        /* 文档读取结束时会触发这个方法*/
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		Log.i(TAG, "endDocument()");
	}
}

执行的结果为:

(一屏截不完,我分了两屏截下来的)

对比上面的xml文件,我们会发现第一行会触发startDocument()方法;红色部分,前后是成对出现的,前面的部分会触发startElement()方法,后面的部分会触发endElement()方法,当xml文件读取完之后会触发endDocument()方法,这就是上面所说的事件驱动的本质了:从上到下,从左到右,逐一读取xml文件中的内容,读到什么类型的内容就触发对应的方法。

知道了事件驱动的原理之后,再来看一段代码,看看第二段代码中的几个方法的参数具体表示什么,我会在注释中进行说明,结合最后的运行结果,我们就能知道每个参数的表示的意思了

把第二段代码改为:

package com.example.xml_parser;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;

public class SAXForHandler extends DefaultHandler {

	private static final String TAG = "SAX_Parser";
	private String perTag; // 杩欎釜鍙橀噺鐢ㄦ潵璁板綍褰撳墠姝e湪璇诲彇鍒扮殑鏍囩
	StringBuffer strbuf;// 杩欎釜瀛楃涓茬敤鏉ヤ繚瀛樿鍙栧埌鐨勫唴瀹�

	public StringBuffer getString() {
		return strbuf;
	}

	public void startDocument() throws SAXException {
		Log.i(TAG, "startDocumnet()");
		strbuf = new StringBuffer();
	}

	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		/* 参数说明:
		 * uri:名称空间 URI,如果元素没有任何名称空间 URI,或者没有正在执行名称空间处理,则为空字符串;
		 * localName:本地名称(不带前缀),如果没有正在执行名称空间处理,则为空字符串;
		 * qName - 限定的名称(带有前缀),如果限定的名称不可用,则为空字符串
		 * attributes - 附加到元素的属性。如果没有属性,则它将是空的 Attributes 对象*/
		Log.i(TAG, "startElement()");
		System.out.println(uri);
		System.out.println(localName);
		System.out.println(qName);
		System.out.println(attributes);
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		/* 参数说明:
		 * ch - 正在读取的文本节点;
           start - 字符数组中的开始位置;
           length - 从字符数组中使用的字符数。 */
		Log.i(TAG, "characters()");
		System.out.println(ch);
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		/* 参数说明:
		 * uri - 名称空间 URI,如果元素没有任何名称空间 URI,或者没有正在执行名称空间处理,则为空字符串;
           localName - 本地名称(不带前缀),如果没有正在执行名称空间处理,则为空字符串;
           qName - 限定的名称(带有前缀),如果限定的名称不可用,则为空字符串。*/
		Log.i(TAG, "endElement()");
		System.out.println(uri);
		System.out.println(localName);
		System.out.println(qName);
	}

	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		Log.i(TAG, "endDocument()");
	}
}

运行的结果为:(每一页的第一行是上一页的最后一行)

(不知道为什么会出现乱码,我把编码格式改成UTF-8和GBK都会出现乱码,如果哪位高人知道的话,还望不吝赐教)

代码注释中的参数说明是API文档上面的,说的也有点不清不楚的,对比上面两次运行的结果,我们应该不难发现第二段代码中的几个方法的参数的所表示的意思

下面给出完整的第二段代码:

package com.example.xml_parser;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;

public class SAXForHandler extends DefaultHandler {

	private static final String TAG = "SAX_Parser";
	private String perTag; // 这个字符串用来标记正在读取的标签
	StringBuffer strbuf;// 这个字符串用来保存从xml文件中读取到的内容

	public StringBuffer getString() {
		return strbuf;
	}

	public void startDocument() throws SAXException {
		Log.i(TAG, "startDocumnet()");
		strbuf = new StringBuffer();
	}

	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		Log.i(TAG, "startElement()");
		perTag = localName;
		if(perTag == "student"){
			for(int i=0; i<attributes.getLength(); i++){
				strbuf.append(attributes.getLocalName(i)+":"+attributes.getValue(i));
			}
			
		}
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		Log.i(TAG, "characters()");
		if(perTag == "name"){
			strbuf.append(ch);
		} else if(perTag == "grade"){
			strbuf.append(ch);
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		Log.i(TAG, "endElement()");
		if(perTag == "student"){
			strbuf.append("\n");
		}
		perTag = null;
	}

	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		Log.i(TAG, "endDocument()");
	}
}

进行一下几点说明:

1、由于使用SAX解析xml文件的时候,并不能区分什么是有用的内容,什么是没有用的内容,比如说上面说过的\n\t对我们来说就是没有实际的用处,所以在代码中需要添加if语句来判断读取到的内容是不是我们想要的,如果是,则保存下来,如果不是,则直接跳过;

2、每次调用endElement()方法的时候,一定要将用来标记正在读取的内容的perTag清空,如果不清空的话,当程序继续向下运行,将是空的文本节点,将触发characters方法,进行if判断,仍然成立,则会将\n\t也保存到strbuf中。

抱歉!评论已关闭.