SAX 是xml的解析器的一种,没DOM 来的方便,(下面的话直接摘抄啦:)):
- SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口。下面是一些ContentHandler接口常用的方法:
- startDocument()
- 当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。
- endDocument()
- 和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
- startElement(String namespaceURI, String localName, String qName, Attributes atts)
- 当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。
- endElement(String uri, String localName, String name)
- 这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
- characters(char[] ch, int start, int length)
- 这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
以上主要讲的是原理性的东西,这里通过一个例子来说明问题。
一个person类,其 有ID,name age三个属性,保存在xml文件当中,现在需要通过SAX 将保存在xml文件中的值解析出来:
文件目录结构如下:
person.xml 为:
<?xml version="1.0" encoding="utf-8"?>
<persons>
<person id="23">
<name>Andy</name>
<age>28</age>
</person>
<person id="20">
<name>Candy</name>
<age>27</age>
</person>
</persons>
SAXxml.java
解析算法的核心部分
package com.andy.android.saxtest;
import java.util.ArrayList;
import java.util.List;
import com.andy.android.domain.Person;
import java.io.InputStream;
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 SAXxml {
/*
* 解析后返回person的list结构
* @param 输入流,xml 文件的输入流
* */
public List<Person> getPerson(InputStream inputstream) throws Throwable{
//获得解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//从解析器工厂new一个解析器
SAXParser parse = factory.newSAXParser();
PersonHandle perHandle = new PersonHandle();
//解析输入流,解析类使用perHandle类
parse.parse(inputstream, perHandle);
//返回所解析的结果
return perHandle.getPersons();
}
}
/**
*
* @author bluesky
* @function: 定义PersonHandle类,该类继承自DefaultHandle类,解析器在处理输入流的时候遇到相应的字段会自动触发该类中的一些事件。
*
*/
class PersonHandle extends DefaultHandler{
List<Person> persons = null;
private String preTag = null;
private Person person = null;
/**
*
* @return 返回person的列表
*/
public List<Person> getPersons(){
return persons;
}
/**
* 重载函数,解析器遇到文本时自动解析
* @param ch :字符串,
* @param start :字符串的起始部分
* @param length:字符串的长度
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
//如果某个文本前面的标签不为空,证明改文本是我们所关注的。
if(preTag!=null){
//获取文本字符串
String tempData = new String(ch,start,length);
//如果文本前面的标签是name,表明该字段的值是name,是我们需要提取的值
if("name".equals(preTag)){
person.setName(tempData);
}
//如果文本前面的标签是age,表明该字段的值是age,是我们需要提取的值
else if("age".equals(preTag)){
person.setAge(new Short(tempData));
}
}
}
/**
* 文档解析完成,我们不关心这块的值
*/
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
}
/**
* 元素解析完成时自动触发该函数
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
//如果一个person解析完成之后,需要将保存在person 的值add 到List<Person> 里面去。并清空临时的person内容准备为下一次解析做准备。
if("person".equals(localName)){
persons.add(person);
person = null;
}
preTag = null;//恢复初始化
}
@Override
public void startDocument() throws SAXException {
//在文档开始的时候触发该函数,此时我们new一个people的list
persons = new ArrayList<Person>();
}
/**
* 遇到解析元素的时候会自动调用该函数
* @param uri :命名空间。
* @param localName 元素的名字
* @param qName :带命名空间的元素名字。
* @param attributes :属性名
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
//如果第一个元素名为person的话,new一个person,表明遇到一个新的person需要解析了
if("person".equals(localName)){
person = new Person();
//首先获取属性,并设置属性。
Integer id = new Integer(attributes.getValue(0));
person.setId(id);
}
//记录下前一个标签的值,方便在后面的文本解析的时候知道解析的是什么字段。
preTag = localName;
}
}
Person.java
person类得数据结构
package com.andy.android.domain;
public class Person {
private Integer id;
private String name;
private short age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public short getAge() {
return age;
}
public void setAge(short age) {
this.age = age;
}
}
XmlTestActivity.java
package com.andy.android.xmltest;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import com.andy.android.domain.*;
import com.andy.android.saxtest.*;
public class XmlTestActivity extends Activity {
private SAXxml mSAXxml = null;
private List<Person> per = null;
private static final String TAG = "XMLTEST";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSAXxml = new SAXxml();
//获取文件作为输入流
InputStream input = this.getClass().getClassLoader().getResourceAsStream("person.xml");
try {
List<Person> per = mSAXxml.getPerson(input);
for(Person p :per){
//打印解析出来的结果
Log.i(TAG, p.getId()+p.getName()+"\t"+p.getAge());
}
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
主要是
Log.i(TAG, p.getId()+p.getName()+"\t"+p.getAge()); 所打印出来的部分
刚好和我们前面的设置的XML里面的内容一致的。此处证明我们的解析是成功的。
OK了,大功告成~~~
2011-12-11 加 :
如果使用Dom方式来解析的话,需要将所有的内容加载到内从中,这样对内存的要求就比较高,对于手持设备来讲,一般不推荐使用该方式。这里可以仿照上述的方式来使用Dom来解析person的xml文件。
代码如下:
package com.andy.android.domtest;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.andy.android.domain.Person;
public class DomXmltest {
/*
* 解析后返回person的list结构
* @param 输入流,xml 文件的输入流
* */
public List<Person> getPersons(InputStream inputstream) throws Throwable{
List<Person> persons = new ArrayList<Person>();
//Defines a factory API that enables applications to obtain a parser that produces DOM object trees from XML documents.
//定义一个工厂,该工厂是的应用程序获取一个从XML文本中产生DOM对象树的解析器。
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
//从xml文本中获取DOM文档的实例
DocumentBuilder domBuider = domFactory.newDocumentBuilder();
//从输入流解析出来的文本内容
Document document = domBuider.parse(inputstream);
//获取解析文档的根节点
Element root = document.getDocumentElement();
NodeList personList = root.getElementsByTagName("person");
for(int i=0;i<personList.getLength();i++){
//定义一个person 类
Person person = new Person();
//获取一个person的元素
Element personElement = (Element) personList.item(i);
//设置id
person.setId(new Integer(personElement.getAttribute("id")));
//获取person的子元素节点
NodeList personChilds = personElement.getChildNodes();
for(int j=0;j<personChilds.getLength();j++){
//判断当前节点类型是否是元素类型节点
if(personChilds.item(j).getNodeType() == Node.ELEMENT_NODE){
Element child = (Element) personChilds.item(j);
if("name".equals(child.getNodeName())){
person.setName(child.getFirstChild().getNodeValue());
}else if("age".equals(child.getNodeName())){
person.setAge(new Short(child.getFirstChild().getNodeValue()));
}
}
}
persons.add(person);
}
return persons;
}
}
一样达到解析xml的效果,对于xml比较小的文件可以采用该方式进行解析,大的话就不要考虑了。
最后一种讲的是pull 解析XML 文件,这种方式在android系统中庸的比较多,其效率高,编程方便受到了很大的欢迎。下面我们还是按照上面的例子来分两部分说。
1. 解析xml文件,代码中的注释写的比较详细,这里就不再罗嗦了:
public List<Person> getPerson(InputStream inputStream) throws Throwable{
List<Person> persons = null;
Person per = null;
XmlPullParser xmlPullParse = Xml.newPullParser(); //new一个xml的pull解析器
xmlPullParse.setInput(inputStream, "utf-8");//传递输入流给解析器,并制定XML文件的编码格式
int eventType = xmlPullParse.getEventType();//放回当前事件的类型如:(START_TAG, END_TAG, TEXT, etc.)
while(eventType != XmlPullParser.END_DOCUMENT){
switch(eventType){
case XmlPullParser.START_DOCUMENT:
persons = new ArrayList<Person>();//文档开始处,
break;
case XmlPullParser.START_TAG:
String tagName = xmlPullParse.getName();//获取当前tag的名称,
if("person".equals(tagName)){//persons 我们不关心,所以忽略
per = new Person();
per.setId(new Integer(xmlPullParse.getAttributeValue(0)));//设置person的id
}
if(per != null){
if("name".equals(tagName)){
per.setName(xmlPullParse.nextText());//取得当前TAG的后面的那个文本信息。就是name的值
}
if("age".equals(tagName)){
per.setAge(new Short(xmlPullParse.nextText()));//取得当前TAG的后面的那个文本信息。就是age的值
}
}
break;
case XmlPullParser.END_TAG: //TAG结束时,将person 信息add到person list里面去。
if("person".equals(xmlPullParse.getName())){
persons.add(per);
per = null;
}
default:
break;
}
eventType = xmlPullParse.next(); //触发下一个事件,比如从person tag 到name tag
}
return persons;
}
同样可以将结果解析出来。
2. 生成XML文件,生成的话感觉是更简单一点,用一个xml的序列化对象,然后将该对象的数据写入到xml文件中去:
public static void savePerson(OutputStream outputStream,List<Person> pers) throws Throwable{
XmlSerializer xmlSer = Xml.newSerializer();//new 一个xml的序列化对象
xmlSer.setOutput(outputStream, "utf-8");
xmlSer.startDocument("UTF-8", true); //xml文件的开始
xmlSer.startTag(null, "persons");
for(Person per:pers){ //从该for循环中不断将值push到xml文件中去
xmlSer.startTag(null, "person");
xmlSer.attribute(null, "ID", per.getId().toString());//属性ID
xmlSer.startTag(null, "name"); //写入name属性
xmlSer.text(per.getName().toString());
xmlSer.endTag(null, "name");
xmlSer.startTag(null, "age");//写入age属性
xmlSer.text(new Short(per.getAge()).toString());
xmlSer.endTag(null, "age");
xmlSer.endTag(null, "person");
}
xmlSer.endDocument();//文档结束
outputStream.flush();//写入到文件
outputStream.close();//关闭输出流
}
可以到activity文件中做测试,这里new了一个china.xml文件,测试结果如下:
可以看到的确是生成了对应的xml文件。
平时工作中推荐用pull来进行xml文件的操作。比较简单。