Android中XML解析之XMLSAXParser解析

XML解析有幾種方式,本文主要介紹XML解析之XMLSAXParser解析。XMLSAXParser解析主要涉及到SAXParserFactory類、SAXParser類和DefaultHandler類。主要使用了工廠方法模式,多例模式,缺省的適配器模式和以及觀察者模式。SAXParser解析的靜態結構類圖,如下:
SAXParser解析的靜態結構類圖
1 SAXParserFactory工廠類和SAXParser類
SAXParserFactory和SAXParser都是抽象類,不能進行實例化。利用多態性,SAXParserFactory類的設計採用退化的工廠方法模式,SAXParserFactory的靜態工廠方法newInstance()通過返回自身子類的一個實例,來得到工廠對象。代碼如下:

public static SAXParserFactory newInstance() {
        // 直接實例化該類,而不是使用反射機制
        return new SAXParserFactoryImpl();
}

獲得工廠對象之後,利用該對象,調用newSAXParser()方法,進而獲得SAXParser對象。

    @Override
    public SAXParser newSAXParser() throws ParserConfigurationException {
        if (isValidating()) {
            throw new ParserConfigurationException(
                    "No validating SAXParser implementation available");
        }

        try {
            return new SAXParserImpl(features);
        } catch (Exception ex) {
            throw new ParserConfigurationException(ex.toString());
        }
}

2 SAXParser類
SAXParser類沒有公共的構造器。也就是說,該類的設計不允許客戶端對其進行實例化。代碼如下:

 protected SAXParser () {

    }

並且它是一個抽象類。因此在具體類SAXParserFactoryImpl得到SAXParser類型對象時,實際實例化的是SAXParser類的子類SAXParserImpl類。SAXParser類帶有多個重載的parser()方法,可以從各種源來解析XML文件,包括輸入流,文件,字符串等。
3 DefaultHandler類
DefaultHandler類實現了自ContextHandler接口。該接口是SAX2解析的核心,它共有11個方法,但並不是每個方法都會在解析中用到。如果解析的具體子類都實現ContextHandler接口,那麼每個子類都必須實現每一個方法,不管它想不想使用,都必須實現。因此,爲了能夠根據用戶的需求,只呈現給用戶想要的方法,提供了抽象的DefaultHandler類。該類的所有方法(多數,該類提供了幾個自己的方法)都是ContextHandler接口接口中方法的空實現。這樣子類,就可以根據自己的需要重寫必須的方法,而不必實現所有的方法。這很顯然是一種缺省適配模式。下面對幾個核心的解析方法作分析:
3.1 開始文檔 startDocument ()

 public void startDocument ()
    throws SAXException
    {
    // no op
    }

該方法接收文檔開始的通知。可以在該方法中,實現一些特定的行爲,比如分配樹的根節點(root node),或創建一個輸出文件。如果解析出錯,會拋出一個SAXException異常。
3.2 開始元素 startElement(String uri,String localName,String qName,Attributes attributes)

   public void startElement (String uri, String localName,
                  String qName, Attributes attributes)
    throws SAXException
    {
    // no op
    }

該方法接收元素開始的通知(即由XML中的元素觸發)。在該方法中可以實現,比如分配一個新的樹節點(a new tree node),或寫輸出流到一個文件中。該方法包含4個參數:
uri 命名空間的URI 或 如果沒有命名空間或如果命名空間的解析沒有執行就是空的字符串(””)
localName 沒有前綴的本地名 如果命名空間解析沒有解析也是空字符串(””)
qName 帶有前綴的完全限定名 如果限定名不可用也是空的字符串(””)
attributes 元素的屬性集 如果沒有屬性,就是空的屬性對象。
3.3 字符方法characters(char ch[],int start,int length)

    public void characters (char ch[], int start, int length)
    throws SAXException
    {
    // no op
    }

該方法接收元素內部字符數據的通知(由元素的內容觸發)。在該方法中,可以對其字符內容進行處理,比如添加數據到一個節點或緩衝區buffer中,或者將其打印到文件。該方法包含3個參數:
ch[] 字符數組 是元素內容的字符數組
start 字符數組的開始位置
length 字符個數
3.4 結束方法 endElement(String uri,String localName,String qName)

    public void endElement (String uri, String localName, String qName)
    throws SAXException
    {
    // no op
    }

該方法接收元素結束通知(由結尾標籤觸發)。在該方法中,可以進行的操作,比如,結束以個樹節點,或寫輸出流到文件。
3.5 文檔結束 endDocument ()

    public void endDocument ()
    throws SAXException
    {
    // no op
    }

該方法接收文檔結束通知(由文檔結束觸發)。在該方法中的操作,比如結束樹,或關閉輸出流文件。
子類可以重寫上面的5個方法,來進行自己的實現。
4 SAXParser.parser()方法
該方法的重載形式很多,但最終都是調用的parse(InputSource is, DefaultHandler dh)

   public void parse(InputSource is, DefaultHandler dh)
        throws SAXException, IOException {
        if (is == null) {
            throw new IllegalArgumentException("InputSource cannot be null");
        }

        XMLReader reader = this.getXMLReader();
        if (dh != null) {
            reader.setContentHandler(dh);
            reader.setEntityResolver(dh);
            reader.setErrorHandler(dh);
            reader.setDTDHandler(dh);
        }
        reader.parse(is);
    }

這兒的設計使用了觀察者模式。這兒DefaultHandler的子類是具體觀察者,ContextHandler是抽象觀察者;XMLReader對象是具體主題對象 ,而XMLReader接口是抽象的主題對象。通過setXXX(hd)方法登記觀察者,一旦讀取了XML元素(注意SAX讀取XML文件的方式,即流處理方式[見下文]),就通知相應的觀察對象調用相應的方法進行處理。ContentHandler EntityResolver ErrorHandler DTDHandler 都是接口,分別處理XML元素的不同部分,比如ContentHandler提供了ContextHandler差不多的11個API。
5 SAX中的流處理方式
在遇到一個標識符的時候,它並不會記錄下以前碰到的標識符。也就是說,在startElement()方法中,所有的信息就是標識符的名字和屬性,至於標識符的嵌套結構、上層標識符的名字,是否有子元素等其他與結構相關的信息,都不得而知,需要用戶系統完成,即我們要對標籤進行判斷。
下面是對ConcreteDefaultHandler類進行測試,輸出的兩張截圖:
ConcreteDefaultHandler類測試
ConcreteDefaultHandler類測試
可以看出SAXXMLParser對XML文件的解析過程。它們只針對標籤,不考慮結構。並且如果不對獲取的字符串進行處理,將得到大量的空格和換行符。
下面是一個代碼實例:
Activity_main.xml代碼:

   <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"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/mainAc_btn_Obtain"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="獲取XML文件" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <TextView
            android:id="@+id/mainAc_tv_showContent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="SADF" />
    </ScrollView>

    <Button
        android:id="@+id/mainAc_btn_SAX"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pull解析獲取的XML文件" />

    <ListView
        android:id="@+id/mainAc_lv_showSAXParserContent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >
    </ListView>

</LinearLayout>

item.xml代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="id:" />

        <TextView
            android:id="@+id/item_mainAc_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="name:" />

        <TextView
            android:id="@+id/item_mainAc_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="version:" />

        <TextView
            android:id="@+id/item_mainAc_version"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>

MainActivity代碼:

package com.luise.android_2015_xmlparser_sax;

import java.io.IOException;
import java.io.StringReader;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.xml.sax.InputSource;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.luise.android_2015_xmlsaxparse.adapter.AppAdapter;
import com.luise.android_2015_xmlsaxparse.bean.App;

public class MainActivity extends Activity implements OnClickListener {
    // declare member variation
    private Button mObtain;
    private Button mPull;
    private TextView mTvShowObtain;
    private ListView mLvPull;

    private String mResult = null;

    private static final int OBTAIN_XML_FAIL = 0;
    private static final int OBTAIN_XML_SUCCESS = 1;
    private static final int PULL_XML_SUCCESS = 2;
    private static final String FAIL_MESSAGE = "獲取XML文件失敗";

    private List<App> list;

    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case OBTAIN_XML_FAIL:// 獲取XML失敗
                mTvShowObtain.setText(FAIL_MESSAGE);
                break;
            case OBTAIN_XML_SUCCESS:// 獲取XML成功
                mTvShowObtain.setText(mResult);
                break;
            case PULL_XML_SUCCESS:// 解析XML成功
                showParserContent();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mObtain = (Button) findViewById(R.id.mainAc_btn_Obtain);
        mPull = (Button) findViewById(R.id.mainAc_btn_SAX);

        mTvShowObtain = (TextView) findViewById(R.id.mainAc_tv_showContent);
        mLvPull = (ListView) findViewById(R.id.mainAc_lv_showSAXParserContent);

        mObtain.setOnClickListener(this);
        mPull.setOnClickListener(this);
    }

    protected void showParserContent() {
        mLvPull.setAdapter(new AppAdapter(MainActivity.this, list));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.mainAc_btn_Obtain:
            // 在子線程中,獲取服務器上的xml文件
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mResult = getXML();
                    // 發送handler處理消息
                    if (mResult == null || "".equals(mResult)) {
                        mHandler.sendEmptyMessage(0);
                    } else {
                        mHandler.sendEmptyMessage(1);
                    }
                }
            }).start();

            break;
        case R.id.mainAc_btn_SAX:
            parserXML();
            mHandler.sendEmptyMessage(2);
            break;
        }
    }

    private void parserXML() {
        if (mResult == null) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mResult = getXML();
                }
            }).start();
        }
        // 獲得SAXParserFactory工廠的實例
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        try {
            // 獲得SAXParser實例
            SAXParser saxParser = saxParserFactory.newSAXParser();
            SAXHandler saxHandler = new SAXHandler();
            // 解析
            saxParser.parse(new InputSource(new StringReader(mResult)),
                    saxHandler);
            list = saxHandler.getList();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    protected String getXML() {
        String result = null;
        HttpGet httpGet = new HttpGet("http://192.168.15.196/app.xml");
        HttpClient httpClient = new DefaultHttpClient();
        try {
            HttpResponse httpResponse = httpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity httpEntity = httpResponse.getEntity();
                result = EntityUtils.toString(httpEntity, "UTF-8");
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
        return result;
    }
}

SAXHandler.java代碼:
package com.luise.android_2015_8_25_xmlparser_sax;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.luise.android_2015_8_25_xmlsaxparse.bean.App;

public class SAXHandler extends DefaultHandler {
private String element;
private List list;
private App app;
private StringBuilder strBuilder;

@Override
public void startDocument() throws SAXException {
    super.startDocument();
    list = new ArrayList<App>();
    strBuilder = new StringBuilder();
}

@Override
public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
    element = localName;
}

@Override
public void characters(char[] ch, int start, int length)
        throws SAXException {
    if (element.equals("app")) {
    } else if (element.equals("id")) {
        strBuilder.append(ch, start, length);
    } else if (element.equals("name")) {
        strBuilder.append(ch, start, length);
    } else if (element.equals("version")) {
        strBuilder.append(ch, start, length);
    }
}

@Override
public void endElement(String uri, String localName, String qName)
        throws SAXException {
    String strText = strBuilder.toString().trim();
    if (localName.equals("app")) {
        list.add(app);
    } else if (localName.equals("id")) {
        app.setId(strText);
    } else if (localName.equals("name")) {
        app.setName(strText);
    } else if (localName.equals("version")) {
        app.setVersion(strText);
    }
    strBuilder.setLength(0);
}

@Override
public void endDocument() throws SAXException {
}

public List<App> getList() {
    return list;
}

}
App.java代碼:
package com.luise.android_2015_8_25_xmlsaxparse.bean;

import java.io.Serializable;

public class App implements Serializable {
private String id;
private String name;
private String version;

public App() {

}

public App(String id, String name, String version) {
    this.id = id;
    this.name = name;
    this.version = version;
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getVersion() {
    return version;
}

public void setVersion(String version) {
    this.version = version;
}

}
AppAdapter代碼:

package com.luise.android_2015_xmlsaxparse.adapter;

import java.util.List;

import com.luise.android_2015_xmlparser_sax.R;
import com.luise.android_2015_xmlsaxparse.bean.App;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class AppAdapter extends BaseAdapter {
    private Context mCtx;
    private List<App> mList;

    private LayoutInflater mInflater;

    public AppAdapter(Context context, List<App> list) {
        mCtx = context;
        mList = list;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        mInflater = LayoutInflater.from(mCtx);
        convertView = mInflater.inflate(R.layout.item_mainactivity_layout,
                parent, false);

        TextView id = (TextView) convertView.findViewById(R.id.item_mainAc_id);
        TextView name = (TextView) convertView
                .findViewById(R.id.item_mainAc_name);
        TextView version = (TextView) convertView
                .findViewById(R.id.item_mainAc_version);

        id.setText(mList.get(position).getId());
        name.setText(mList.get(position).getName());
        version.setText(mList.get(position).getVersion());

        return convertView;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章