XML结构
<title> hello </title>
标签开始 文本 标签结束
事件模型
SAX处理XML数据是采用事件的形式来处理,下面我们来简单的做个介绍:
当我们处理XML数据中遇到一个开始标签后SAX会告诉你“我遇到了一个开始标签,这个标签是XXXX”,等你作出反应后,它会继续往下,这时它遇到了一段文本,SAX告诉你“我遇到了一段文本,是XXXX”,然后继续等你作出反应,接着下去就遇到了结束标签,SAX仍然会告诉你“我到了一个结束标签是XXX”。SAX就是以这样的方式将整个XML数据全部处理完。
为何使用SAX解析XML
- 节约内存
- 效率高
SAX解析XML文件采用的是事件驱动,它不需要解析整个文档,而是在解析文档的过程中,判断读到的字符是否符合XML语法的某部分,符合的话就会触发事件(回调方法)。
相关类或方法
类或方法 | 描述 |
---|---|
DefaultHandler类 | 安卓中内置的用于SAX处理XML的类,但是大多情况下我们都需要继承该类重写部分方法,才能达到处理XML数据的功能。 |
startDocument | 每处理一个XML文档都会响应一次。所以这个方法里可以写需要初始化的代码。 |
startElement | 处理每个节点所触发的方法。 |
characters | 处理一个节点之间的文本时候触发该方法,但是该方法并不会告诉你当前文本的所属标签,而仅仅是告诉你文本内容。 |
endElement | 遇到一个节点的结束标签时,将会出发这个方法,并且会传递结束标签的名称。 |
endDocument | 如果当前的XML文档处理完毕后,将会触发该方法,在此方法内你可以将最终的结果保存并且销毁不需要使用的变量。 |
执行流程
xml文件
<notic>
<id>1</id>
<title>%3cs%3edsds%3c%2fs%3e</title>
<content>%e5%86%85%e5%ae%b91</content>
<author>1</author>
</notic>
响应过程:
方法名称 | localName(标签名称) | ch[](文本名称) |
---|---|---|
startDocument | -- | -- |
startElement | notic | -- |
startElement | id | -- |
characters | -- | 1 |
endElement | id | -- |
startElement | title | -- |
characters | -- | %3cs%3edsds%2c%2fs%3e |
endElement | title | -- |
startElement | content | -- |
characters | -- | %e5%86%85%e5%ae%b91 |
endElement | content | -- |
startElement | author | -- |
characters | -- | 1 |
endElement | author | -- |
endElement | notic | -- |
endDocument | -- | -- |
localName和qName的区别
<?xml version="1.0" encoding="utf-8"?>
<websites
xmlns:sina="http://www.sina.com"
xmlns:baidu="http://www.baidu.com">
<sina:website sina:blog="blog.sina.com">新浪</sina:website>
<baidu:website baidu:blog="hi.baidu.com">百度</baidu:website>
</websites>
Namespace(空间名称,命名空间)
引入的原因是为了避免混淆。例如上面的这个XML文档,sina和baidu都有blog属性,定义了两个namespace,就像sax官网说的,用namespace是为了实现更多的扩展功能,作为基本应用,很多时候都用不到它:
sina的namespace: http://www.sina.com
baidu的namespace:http://www.baidu.com
xmlns:sina="http://www.sina.com"
xmlns:baidu="http://www.baidu.com">
namespace的值可以任意,但是注意不要重复。一般默认的格式都是以url来作为namespace,比如
xmlns:android="http://schemas.android.com/apk/res/android。
Prefix(前缀)
sina:blog中 sina就是前缀。
LocalName(本地名称)
sina:blog 中blog就是localName。
QName(Qualified Name 限定?指定?名称)
sina:blog就是QName,相当于前缀+":"+LocalName。
uri(不是url)
例如sina:blog的uri就是前缀sina的namespace,即"http://www.sina.com"。
例子
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee>
<id>2163</id>
<name>Kumar</name>
<department>Development</department>
<type>Permanent</type>
<email>kumar@tot.com</email>
</employee>
<employee>
<id>6752</id>
<name>Siva</name>
<department>DB</department>
<type>Contract</type>
<email>siva@tot.com</email>
</employee>
<employee>
<id>6763</id>
<name>Timmy</name>
<department>Testing</department>
<type>Permanent</type>
<email>timmy@tot.com</email>
</employee>
</employees>
Employee类
public class Employee {
private String name;
private int id;
private String department;
private String type;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return id + ": " + name;
}
public String getDetails() {
String result = id + ": " + name + "\n" + department + "-" + type
+ "\n" + email;
return result;
}
}
创建一个类继承DefaultHandler类
public class SAXXMLHandler extends DefaultHandler {
private List<Employee> employees;
private String tempVal;
private Employee tempEmp;
public SAXXMLHandler() {
employees = new ArrayList<Employee>();
}
public List<Employee> getEmployees() {
return employees;
}
// Event Handlers
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// reset
tempVal = "";
if (qName.equalsIgnoreCase("employee")) {
// create a new instance of employee
tempEmp = new Employee();
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
tempVal = new String(ch, start, length);
}
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (qName.equalsIgnoreCase("employee")) {
// add it to the list
employees.add(tempEmp);
} else if (qName.equalsIgnoreCase("id")) {
tempEmp.setId(Integer.parseInt(tempVal));
} else if (qName.equalsIgnoreCase("name")) {
tempEmp.setName(tempVal);
} else if (qName.equalsIgnoreCase("department")) {
tempEmp.setDepartment(tempVal);
} else if (qName.equalsIgnoreCase("type")) {
tempEmp.setType(tempVal);
} else if (qName.equalsIgnoreCase("email")) {
tempEmp.setEmail(tempVal);
}
}
}
创建XML解析类
public class SAXXMLParser {
public static List<Employee> parse(InputStream is) {
List<Employee> employees = null;
try {
// create a XMLReader from SAXParser
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser()
.getXMLReader();
// create a SAXXMLHandler
SAXXMLHandler saxHandler = new SAXXMLHandler();
// store handler in XMLReader
xmlReader.setContentHandler(saxHandler);
// the process starts
xmlReader.parse(new InputSource(is));
// get the `Employee list`
employees = saxHandler.getEmployees();
} catch (Exception ex) {
Log.d("XML", "SAXXMLParser: parse() failed");
}
// return Employee list
return employees;
}
}
使用SAXXMLParser
List<Employee> employees = SAXXMLParser.parse(getAssets().open("employees.xml"));