XML解析有幾種方式,本文主要介紹XML解析之XMLSAXParser解析。XMLSAXParser解析主要涉及到SAXParserFactory類、SAXParser類和DefaultHandler類。主要使用了工廠方法模式,多例模式,缺省的適配器模式和以及觀察者模式。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類進行測試,輸出的兩張截圖:
可以看出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;
}
}