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;
    }

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