Android 通过Dom, Sax, Pull解析网络xml数据

这篇文章不是完全原创,XML解析的部分参考了 liuhe688 的文章。文章地址:http://blog.csdn.net/liuhe688/article/details/6415593

这是一个几乎完整的过程。覆盖面也比较广。应用了AsyncTask, BaseAdapter, ListView, Dom, Sax, Pull等,这些也是开发应用中必不可少的部分。

效果图:

Android 通过Dom, Sax, Pull解析网络xml数据  Android 通过Dom, Sax, Pull解析网络xml数据  Android 通过Dom, Sax, Pull解析网络xml数据

首先,做了些准备工作。down了一份xml数据放在本地服务器上

Android 通过Dom, Sax, Pull解析网络xml数据

定义了一个model。

book.java

package com.example.phonedemo.model;

public class Book {
    private int id;
    private String name;
    private float price;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

}

1、客户端发起请求,

这是通过AsyncTask异步发起的请求。

AsyncTask 主要需要复写四个方法:

onProExecute():这个方法会在UI线程中调用 execute(..)之后立即执行,在开发中通常做一些初始化和加载UI显示的工作。

doInBackground(String... params):比较费时的操作,可以放在这里。网络请求等...

onProgressUpdate(Integer... values):通常,我们在耗时操作时候,比如download,会有一个百分比进度。在doInBackground()方法中调用

publishProgress()方法后,系统会调用此方法,更新进度信息。

onPostExecute(InputStream result):执行完毕以后调用的方法。

2、服务器端返回xml数据,

3、客户端解析数据,最终呈现出来

我用了三种解析方法,参照上述blog的地址。不过,在他得代码中稍有问题。ID是没有的。我在我的代码中改正了。

三种方式都是解析一个xml,所以写了一个接口文件。

BookParserImpl.java

package com.example.phonedemo.util;

import java.io.InputStream;
import java.util.List;

import com.example.phonedemo.model.Book;


public interface BookParserImpl {
    public List<Book> parse(InputStream is);
    
    public String serialize(List<Book> books);
}

然后,三种解析分别实现它即可。

Dom解析方式

优点:功能齐全,强大。缺点:因为需要完全加载文档,导致,比较耗资源。前辈说j2ee中用的较多。

我虽然也学Java,但是一直都与前台打交道。曾经学的东西,工作中不用,就全忘了。罪过,罪过...

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 DocumentBuilder builder = factory.newDocumentBuilder();
 Document doc = builder.parser(InputStream input);

上述dom初始化解析实例的代码。

DomBookParser.java

package com.example.phonedemo.util;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.example.phonedemo.model.Book;

public class DomBookParser implements BookParserImpl {

    @Override
    public List<Book> parse(InputStream is) {
        List<Book> list = new ArrayList<Book>();
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = null;
        Document doc = null;
        try {
            builder = factory.newDocumentBuilder();
            doc = builder.parse(is);
            NodeList itemList = doc.getDocumentElement().getElementsByTagName(
                    "book");
            for (int i = 0; i < itemList.getLength(); i++) {
                Book book = new Book();
                Node node = itemList.item(i);
                NodeList child = node.getChildNodes();
                NamedNodeMap attr = node.getAttributes();
                book.setId(Integer.parseInt(attr.getNamedItem("id")
                        .getNodeValue()));
                for (int j = 0; j < child.getLength(); j++) {
                    Node childItem = child.item(j);
                    String nodeName = childItem.getNodeName();
                    if (nodeName.equals("name")) {
                        book.setName(childItem.getFirstChild().getNodeValue());
                    } else if (nodeName.equals("price")) {
                        book.setPrice(Float.parseFloat(childItem
                                .getFirstChild().getNodeValue()));
                    }
                }

                list.add(book);
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return list;
    }

    @Override
    public String serialize(List<Book> books) {
        // TODO Auto-generated method stub
        return null;
    }
}

Sax解析方式

sax是事件驱动型的,比起dom来更加灵巧。也更加简单快速。

SaxBookParser.java

package com.example.phonedemo.util;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

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

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

import com.example.phonedemo.model.Book;

public class SaxBookParser implements BookParserImpl {

    @Override
    public List<Book> parse(InputStream is) {
        // TODO Auto-generated method stub
        SAXParserFactory factory = SAXParserFactory.newInstance();
        MyHandler handler = null;
        try {
            SAXParser parser = factory.newSAXParser();
            handler = new MyHandler();
            parser.parse(is, handler);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return handler.getBooks();
    }

    /**
     * <b>解析xml的辅助类</b><br />
     * <b>startDocument()</b> 当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。<br />
     * <b>endDocument()</b> 和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。 <br/>
     * <b>startElement(String namespaceURI, String localName, String qName,
     * Attributes atts)</b>当读到一个开始标签的时候,会触发这个方法。<br />
     * <b>endElement(String uri, String localName, String name)</b>
     * 这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。<br />
     * <b>characters(char[] ch, int start, int length)</b>
     * 这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,<br />
     * 后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
     * 
     * @author qxj
     * 
     */
    class MyHandler extends DefaultHandler {

        private List<Book> books;
        private Book book;
        private StringBuilder builder;

        public List<Book> getBooks() {
            return books;
        }

        /**
         * 接收元素中字符数据的通知。
         * 
         * @param ch
         *            - 字符。
         * @param start
         *            - 字符数组中的开始位置。
         * @param length
         *            - 从字符数组中使用的字符数。
         */
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            // TODO Auto-generated method stub
            super.characters(ch, start, length);
            builder.append(ch, start, length);
        }

        /**
         * 接收元素结束的通知
         * 
         * @param uri
         *            - 名称空间 URI,如果元素没有任何名称空间 URI,或者没有正在执行名称空间处理,则为空字符串。
         * @param localName
         *            - 本地名称(不带前缀),如果没有正在执行名称空间处理,则为空字符串。
         * @param qName
         *            - 限定的名称(带有前缀),如果限定的名称不可用,则为空字符串。
         * @exception SAXException
         *                - 任何 SAX 异常,可能包装另外的异常。
         */
        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            // TODO Auto-generated method stub
            super.endElement(uri, localName, qName);
            if (localName.equals("book")) {
                books.add(book);
            } else if (localName.equals("name")) {
                book.setName(builder.toString());
            } else if (localName.equals("price")) {
                book.setPrice(Float.parseFloat(builder.toString()));
            }
        }

        /**
         * 接收文档的开始的通知
         * 
         * @exception - SAXException - 任何 SAX 异常,可能包装另外的异常
         */
        @Override
        public void startDocument() throws SAXException {
            // TODO Auto-generated method stub
            super.startDocument();
            books = new ArrayList<Book>();
            builder = new StringBuilder();
        }

        /**
         * 接收元素开始的通知。
         * 
         * @param uri
         *            - 名称空间 URI,如果元素没有任何名称空间 URI,或者没有正在执行名称空间处理,则为空字符串。
         * @param localName
         *            - 本地名称(不带前缀),如果没有正在执行名称空间处理,则为空字符串。
         * @param qName
         *            - 限定的名称(带有前缀),如果限定的名称不可用,则为空字符串。
         * @param attributes
         *            - 附加到元素的属性。如果没有属性,则它将是空的 Attributes 对象。
         * @exception SAXException
         *                - 任何 SAX 异常,可能包装另外的异常
         */
        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {
            // TODO Auto-generated method stub
            super.startElement(uri, localName, qName, attributes);
            if (localName.equals("book")) {
                book = new Book();
                if (attributes != null) {
                    book.setId(Integer.parseInt(attributes.getValue("id")));
                }
            }
            builder.setLength(0);
        }

    }

    @Override
    public String serialize(List<Book> books) {
        // TODO Auto-generated method stub
        return null;
    }

}

Pull解析方式

我最喜欢这种解析方式,它最轻巧,简便。android 用到了很多xml的文件,系统都是用pull来解析的。从这个层面中可以看出来。android建议使用这种解析方式。

PullBookParser.java

package com.example.phonedemo.util;

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;

import android.util.Xml;

import com.example.phonedemo.model.Book;

public class PullBookParser implements BookParserImpl {

    @Override
    public List<Book> parse(InputStream is) {
        // TODO Auto-generated method stub
        List<Book> books = null;
        Book book = null;

        XmlPullParser parser = Xml.newPullParser();
        try {
            parser.setInput(is, "UTF-8");
            int eventType = parser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                switch (eventType) {
                case XmlPullParser.START_DOCUMENT:
                    books = new ArrayList<Book>();
                    break;
                case XmlPullParser.START_TAG:
                    if (parser.getName().equals("book")) {
                        book = new Book();
                        book.setId(Integer.parseInt(parser.getAttributeValue(0)));
                    } else if (parser.getName().equals("name")) {
                        parser.next();
                        book.setName(parser.getText());
                    } else if (parser.getName().equals("price")) {
                        parser.next();
                        book.setPrice(Float.parseFloat(parser.getText()));
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if (parser.getName().endsWith("book")) {
                        books.add(book);
                        book = null;
                    }
                    break;
                }
                parser.next();
                eventType = parser.getEventType();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return books;
    }

    @Override
    public String serialize(List<Book> books) {
        // TODO Auto-generated method stub
        XmlSerializer serializer = Xml.newSerializer();
        StringWriter writer = new StringWriter();
        try {
            serializer.setOutput(writer);
            serializer.startDocument("UTF-8", true);
            serializer.startTag("", "books");
            for (Book book : books) {
                serializer.startTag("", "book");
                serializer.attribute("", "id", String.valueOf(book.getId()));

                serializer.startTag("", "name");
                serializer.text(book.getName());
                serializer.endTag("", "name");

                serializer.startTag("", "price");
                serializer.text(book.getPrice() + "");
                serializer.endTag("", "price");
                serializer.endTag("", "book");
            }
            serializer.endTag("", "books");
            serializer.endDocument();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("doc: " + writer.toString());
        return writer.toString();
    }
}

最后,附上Activity

package com.example.phonedemo;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.phonedemo.model.Book;
import com.example.phonedemo.util.DomBookParser;
import com.example.phonedemo.util.PullBookParser;
import com.example.phonedemo.util.SaxBookParser;

public class XMLParserDemo extends Activity {

    private static final String TAG = "XMLParserDemo";

    private static final String PATH = "http://192.168.1.121:8080/wwwroot/android/book.html";

    private List<Book> list = null;
    private Button dom = null;
    private Button sax = null;
    private Button pull = null;
    private Button clear = null;
    private TextView parser = null;
    private ListView listView = null;

    /**
     * 异步请求数据
     * 
     * @author qinxijuan
     * 
     */
    private class MyAsyncTask extends AsyncTask<String, Integer, InputStream> {

        // 表示当前使用哪一种解析方式,0:dom ; 1: sax; 2:pull
        private int flag = 0;

        public MyAsyncTask(int flag) {
            this.flag = flag;
        }

        @Override
        protected void onPostExecute(InputStream result) {
            if (result != null) {
                if (flag == 0) {
                    domParserFromInputStream(result);
                } else if (flag == 1) {
                    saxParserFromInputStream(result);
                } else if (flag == 2) {
                    pullParserFromInputStream(result);
                }
            } else {
                Toast.makeText(XMLParserDemo.this, "数据获取失败!",
                        Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute() Loading ... ");
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
        }

        @Override
        protected InputStream doInBackground(String... params) {

            InputStream result = null;
            HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(params[0]);
            HttpResponse response = null;
            try {
                response = client.execute(get);
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    HttpEntity entity = response.getEntity();
                    result = entity.getContent();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return result;
        }

    }

    static class ViewHolder {
        TextView id;
        TextView name;
        TextView price;
    }

    class MyBaseAdapter extends BaseAdapter {

        private List<Book> list = null;
        private LayoutInflater ly = null;

        public MyBaseAdapter(Context context, List<Book> list) {
            this.ly = LayoutInflater.from(context);
            this.list = list;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return this.list.size();
        }

        @Override
        public Object getItem(int arg0) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public long getItemId(int arg0) {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup arg2) {
            ViewHolder viewHolder = null;
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = ly.inflate(R.layout.simpleitem, null);
                viewHolder.id = (TextView) convertView.findViewById(R.id.id);
                viewHolder.name = (TextView) convertView
                        .findViewById(R.id.name);
                viewHolder.price = (TextView) convertView
                        .findViewById(R.id.age);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            viewHolder.id.setText(String.valueOf(list.get(position).getId()));
            viewHolder.name.setText(list.get(position).getName());
            viewHolder.price.setText(String.valueOf(list.get(position)
                    .getPrice()));
            return convertView;
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.xml_parser_layout);

        this.dom = (Button) super.findViewById(R.id.dom);
        this.sax = (Button) super.findViewById(R.id.sax);
        this.pull = (Button) super.findViewById(R.id.pull);
        this.clear = (Button) super.findViewById(R.id.clear);
        this.parser = (TextView) super.findViewById(R.id.parser);
        this.listView = (ListView) super.findViewById(R.id.list_view);

        this.clear.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                list = new ArrayList<Book>();
                MyBaseAdapter adapter = new MyBaseAdapter(XMLParserDemo.this,
                        list);
                listView.setAdapter(adapter);
                parser.setText("数据清除完毕");
            }
        });

        this.dom.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                MyAsyncTask mTask = new MyAsyncTask(0);
                mTask.execute(PATH);
                parser.setText("使用dom方式解析");
            }
        });

        this.sax.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                MyAsyncTask mTask = new MyAsyncTask(1);
                mTask.execute(PATH);
                parser.setText("使用sax方式解析");
            }
        });

        this.pull.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                MyAsyncTask mTask = new MyAsyncTask(2);
                mTask.execute(PATH);
                parser.setText("使用pull方式解析");
            }
        });
    }

    public void domParserFromInputStream(InputStream input) {
        list = new ArrayList<Book>();
        DomBookParser domParser = new DomBookParser();
        System.out.println("input: " + input.toString());
        list = domParser.parse(input);
        MyBaseAdapter adapter = new MyBaseAdapter(this, list);
        this.listView.setAdapter(adapter);
    }

    public void saxParserFromInputStream(InputStream input) {
        list = new ArrayList<Book>();
        SaxBookParser saxParser = new SaxBookParser();
        list = saxParser.parse(input);
        MyBaseAdapter adapter = new MyBaseAdapter(this, list);
        this.listView.setAdapter(adapter);
    }

    public void pullParserFromInputStream(InputStream input) {
        PullBookParser pullParser = new PullBookParser();
        list = pullParser.parse(input);
        MyBaseAdapter adapter = new MyBaseAdapter(this, list);
        this.listView.setAdapter(adapter);
    }

}

xml_parser_layout.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" >

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

        <Button
            android:id="@+id/dom"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Dom" />

        <Button
            android:id="@+id/sax"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Sax" />

        <Button
            android:id="@+id/pull"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="pull" />

        <Button
            android:id="@+id/clear"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="clear" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/parser"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text=" " />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <ListView
            android:id="@+id/list_view"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >
        </ListView>
    </LinearLayout>

</LinearLayout>