Android之SAX解析XML

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数据:

Android之SAX解析XML_第1张图片

这里我是用自己电脑上的Tomcat搭建的服务器,文件存放地址:TomCat安装目录/Tomcat7.0/webapps/ROOT/get_data.xml.

实现效果:

Android之SAX解析XML_第2张图片

(1)activity_main.xml文件




    

(2)ContentHandler.java

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解析的过程:

先看一下上面项目运行后的日志:

Android之SAX解析XML_第3张图片

1.xml解析开始,startDocument被调用,这个方法在整个XML文件解析过程中只调用一次,我们可以在这个函数中做一些准备,比如说初始化变量等。

2.当解析一个标签的时候,如果该标签有子标签,则先回调该标签的startElement方法,然后触发characters解析该标签的内容,然后子标签触发startElement-characters-endElement(子标签触发),最后该标签触发endElement,该标签解析结束。(这个过程类似与递归,必须明白这一句话。)

下面我来画张图帮助理解一下:

Android之SAX解析XML_第4张图片

看完上图,可以理解SAX解析XMl的过程。


你可能感兴趣的:(android学习笔记,Android,SAX解析XML,SAX,XML,解析XML格式数据)