SAX 全称“Simple API for XML”,是以事件流来解读xml,也就是说:它不需要读入整个文档,而是文档的读入过程中边读边触发事件函数完成解析。所以用它解析XML速度很快,占用内存小,适合解析较大XML文件。
解析之前需要向XMLReader注册—ContentHandler,也就是事件监听器,它必须继承自DefaultHandler类,实现下面我五个方法;
/**
* 在开始XML解析的时候调用
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
}
/**
* 开始解析某个节点的时候调用
* @param uri xml文档的命名空间
* @param localName 当前标签的名字
* @param qName 带命名空间的标签名字
* @param attributes 标签的属性集
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
}
/**
* 获取节点中内容的时候调用
* @param ch 当前读取到的TextNode(文本节点)的字节数组
* @param start 字节开始的位置,为0则读取全部
* @param length 当前TextNode的长度
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
}
/**
* 完成解析某个节点的时候调用
* @param uri xml文档的命名空间
* @param localName 当前结束标签的名字
* @param qName 带命名空间标签的名字
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
}
/**
* 完成整个XML解析的时候调用
* @throws SAXException
*/
@Override
public void endDocument() throws SAXException {
}
SAX解析步骤:
1.得到XML的输入流。
2.得到SAX解析工厂。( SAXParserFactory factory=SAXParserFactory.newInstance();)
3.有解析工厂产生一个SAX解析器。(XMLReader xmlReader=factory.newSAXParser().getXMLReader();)
4.将ContentHandler的实例设置到XMLReader中。(xmlReader.setContentHandler(handler);)
5.开始执行解析。(xmlReader.parse(inputStream));)
下面我们通过例子,来学习SAX解析XML文件
服务器放的XML数据:
这里我是用自己电脑上的Tomcat搭建的服务器,文件存放地址:TomCat安装目录/Tomcat7.0/webapps/ROOT/get_data.xml.
实现效果:
(1)activity_main.xml文件
package com.example.xmlandsax;
import android.util.Log;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
public class ContentHandler extends DefaultHandler {
private final static String TAG="AndroidSaxParseXML";
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
private List app;
public List getApp(){
return app;
}
/**
* 在开始XML解析的时候调用
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
super.startDocument();
Log.i(TAG,"startDocument");
id=new StringBuilder();
name=new StringBuilder();
version=new StringBuilder();
app=new ArrayList();
}
/**
* 开始解析某个节点的时候调用
* @param uri xml文档的命名空间
* @param localName 标签的名字
* @param qName 带命名空间的标签名字
* @param attributes 标签的属性集
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
Log.i(TAG,"startElement="+localName);
//记录当前节点名
nodeName=localName;
}
/**
* 获取节点中内容的时候调用
* @param ch 当前读取到的TextNode(文本节点)的字节数组
* @param start 字节开始的位置,为0则读取全部
* @param length 当前TextNode的长度
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
Log.i(TAG,(new String(ch,start,length)).toString().trim());
//根据当前的节点名判断将内容添加到哪一个StringBuilder对象中
if("id".equals(nodeName)){
id.append(ch,start,length);
}else if("name".equals(nodeName)){
name.append(ch,start,length);
}else if("version".equals(nodeName)){
version.append(ch,start,length);
}
}
/**
* 完成解析某个节点的时候调用
* @param uri xml文档的命名空间
* @param localName 当前结束标签的名字
* @param qName 带命名空间标签的名字
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
Log.i(TAG,"EndElement="+localName);
if("app".equals(localName)){
app.add("id is"+id.toString().trim()+",name is"+name.toString().trim()+",version is"+version.toString().trim()+";\n");
//最后要将StringBuilder清空掉
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
/**
* 完成整个XML解析的时候调用
* @throws SAXException
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
Log.i(TAG,"endDocument");
}
}
(3)MainActivity.java
package com.example.xmlandsax;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import java.io.StringReader;
import java.util.List;
import javax.xml.parsers.SAXParserFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private TextView response_text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
response_text = (TextView) this.findViewById(R.id.response_text);
}
public void click(View v) {
sendRequestWithHttpURLConnection();
}
public void sendRequestWithHttpURLConnection() {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:8080/get_data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithSax(responseData);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void parseXMLWithSax(String xmlData){
try {
//得到SAX解析工厂
SAXParserFactory factory=SAXParserFactory.newInstance();
//由解析工厂生产一个SAX解析器
XMLReader xmlReader=factory.newSAXParser().getXMLReader();
ContentHandler handler=new ContentHandler();
//将ContentHandler的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
List app=handler.getApp();
showResponseToUI(app.toString());
} catch (SAXException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void showResponseToUI(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
//在这里进行UI操作,将结果显示到界面上
response_text.setText(response);
}
});
}
}
(4)build.gradle
网络请求用的是OKHttp,所以要在gradle中加入依赖。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
}
(5)AndroidManifest.xml
需要网络请求权限
根据以上的项目,我们来分析一下SAX解析的过程:
先看一下上面项目运行后的日志:
1.xml解析开始,startDocument被调用,这个方法在整个XML文件解析过程中只调用一次,我们可以在这个函数中做一些准备,比如说初始化变量等。
2.当解析一个标签的时候,如果该标签有子标签,则先回调该标签的startElement方法,然后触发characters解析该标签的内容,然后子标签触发startElement-characters-endElement(子标签触发),最后该标签触发endElement,该标签解析结束。(这个过程类似与递归,必须明白这一句话。)
下面我来画张图帮助理解一下:
看完上图,可以理解SAX解析XMl的过程。