Android之SAX解析XML

来源:互联网 发布:linux c fork 编辑:程序博客网 时间:2024/04/29 11:32

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文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <Button        android:id="@+id/send_request"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="click"        android:text="Send Request" />    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent">        <TextView            android:id="@+id/response_text"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </ScrollView></LinearLayout>

(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<String> app;    public List<String> 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<String>();    }    /**     * 开始解析某个节点的时候调用     * @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<String> 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

需要网络请求权限

 <uses-permission android:name="android.permission.INTERNET" />
根据以上的项目,我们来分析一下SAX解析的过程:

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


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

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

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


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


0 0
原创粉丝点击