• AndroidAndroid解析xml文件


    解析XML的方式有很多种,大家比较熟悉的可能就是DOM解析。

    DOM(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据DOM接口来操作这个树结构了。

      优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。

      缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。

      使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,CPU)。

    为了解决DOM解析存在的问题,就出现了SAX解析。其特点为:

      优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用SAX解析。

      缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。

      使用场合:机器有性能限制。

    SAX解析XML文档采用事件驱动模式。什么是事件驱动模式?它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。

    基于事件驱动的处理模式主要是基于事件源和事件处理器(或者叫监听器)来工作的。一个可以产生事件的对象叫做事件源,而一个可以针对事件做出响应的对象就被叫做事件处理器。

    在SAX接口中,事件源是org.xml.sax包中的XMLReader,他通过parse()方法开始解析XML文档,并根据文档内容产生事件。而事件处理器则是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver这四个接口。他们分别处理事件源在解析过程中产生不同类的事件(其中DTDHandler为解析文档DTD时所用)。详细介绍如下表:

    在上述四个接口中,最重要的就是ContentHandler这个接口,下面是对这个接口方法的说明:

    复制代码
    //设置一个可以定位文档内容事件发生位置的定位器对象


    public void setDocumentLocator(Locator locator)

    //用于处理文档解析开始事件


    public void startDocument()throws SAXException

    //处理元素开始事件,从参数中可以获得元素所在名称空间的uri,元素名称,属性类表等信息


    public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException

    //处理元素结束事件,从参数中可以获得元素所在名称空间的uri,元素名称等信息


    public void endElement(String namespacesURI , String localName , String qName) throws SAXException

    //处理元素的字符内容,从参数中可以获得内容


    public void characters(char[] ch , int start , int length) throws
     SAXException
    
    复制代码

    这里再介绍下XMLReader中的方法。

    复制代码
    //注册处理XML文档解析事件ContentHandler
     
    public void setContentHandler(ContentHandler handler)

    //开始解析一个XML文档
     
    public void parse(InputSorce input) throws
     SAXException
    
    复制代码


    SAX实现实体解析的步骤:

    在android中使用SAX是有迹可循的,完全可以按照下面的方法就可以轻松找到xml里的tag,然后得到想要的内容。具体实现步骤如下:

    (一)第一步:新建一个工厂类SAXParserFactory,代码如下:

    SAXParserFactory factory = SAXParserFactory.newInstance();
    

    (二)第二步:让工厂类产生一个SAX的解析类SAXParser,代码如下:

    SAXParser parser = factory.newSAXParser();
    

    (三)第三步:从SAXPsrser中得到一个XMLReader实例,代码如下:

    XMLReader reader = parser.getXMLReader();
    

    (四)第四步:把自己写的handler注册到XMLReader中,一般最重要的就是ContentHandler,代码如下:

    RSSHandler handler = new RSSHandler();
    reader.setContentHandler(handler);

    (五)第五步:将一个xml文档或者资源变成一个java可以处理的InputStream流后,解析正式开始,代码如下:

    parser.parse(is);
    

    上面几个步骤中,最重要、最关键的就是第四步,handler的实现。

    下面通过一个RSS解析的例子说明handler的实现:

    我们先是自己见一个rss的xml文档,实现本地解析,新建的rss文档如下:

    复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <channel>
    <title>RSS 解析练习</title>
    <description>hehehaha</description>
    <link>http://www.cnblogs.com/felix-hua/</link>
    <language>zh-cn</language>


    <item>
    <title><![CDATA[头条]]></title>
    <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
    <category>0</category>
    <description>描述详细信息的</description>
    <pubDate>2012-01-09</pubDate>
    </item>

    <item>
    <title><![CDATA[新闻]]></title>
    <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
    <category>0</category>
    <description>描述详细信息的</description>
    <pubDate>2012-01-09</pubDate>
    </item>

    <item>
    <title><![CDATA[首页]]></title>
    <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
    <category>0</category>
    <description>描述详细信息的</description>
    <pubDate>2012-01-09</pubDate>
    </item>

    <item>
    <title><![CDATA[财经]]></title>
    <link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
    <category>0</category>
    <description>描述详细信息的</description>
    <pubDate>2012-01-09</pubDate>
    </item>
    复制代码

    建好后,我们命名为rssxml.xml,然后放到项目的根目录下:

    然后我们可以建立两个实体类:

    1、RSSFeed,与完整的xml文档相对应;

    2、RSSItem,与item标签内的信息相对应。

    这样在解析xml时,我们就可以把解析出来的信息放到实体类里,然后直接操作实体类就可以了。下面给出代码:

    RSSFeed.java

    复制代码
    package com.sax.org.entity;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Vector;

    public class RSSFeed {
    private String title;
    private int itemcount;
    private List<RSSItem> itemlist;

    public RSSFeed(){
    itemlist = new Vector<RSSItem>(0);
    }


    public int addItem(RSSItem item){
    itemlist.add(item);
    itemcount++;
    return itemcount;
    }

    public RSSItem getItem(int location){
    return itemlist.get(location);
    }

    public List<RSSItem> getAllItems(){
    return itemlist;
    }


    public List getAllItemForListView(){
    List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
    int size = itemlist.size();
    for(int i=0 ; i<size ; i++){
    HashMap<String , Object> item = new HashMap<String, Object>();
    item.put(RSSItem.TITLE, itemlist.get(i).getTitle());
    item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate());
    data.add(item);
    }
    return data;
    }

    public String getTitle() {
    return title;
    }

    public void setTitle(String title) {
    this.title = title;
    }

    public int getItemcount() {
    return itemcount;
    }

    public void setItemcount(int itemcount) {
    this.itemcount = itemcount;
    }

    public List<RSSItem> getItemlist() {
    return itemlist;
    }

    public void setItemlist(List<RSSItem> itemlist) {
    this
    .itemlist = itemlist;
    }

    }
    复制代码

    RSSItem.java

    复制代码
    package com.sax.org.entity;

    public class RSSItem {
    public static String TITLE = "title";
    public static String PUBDATE = "pubdate";
    public String title;
    public String description;
    public String link;
    public String category;
    public String pubdate;
    public RSSItem() {
    }
    public String getTitle() {
    return title;
    }
    public void setTitle(String title) {
    this.title = title;
    }
    public String getDescription() {
    return description;
    }
    public void setDescription(String description) {
    this.description = description;
    }
    public String getLink() {
    return link;
    }
    public void setLink(String link) {
    this.link = link;
    }
    public String getCategory() {
    return category;
    }
    public void setCategory(String category) {
    this.category = category;
    }
    public String getPubdate() {
    return pubdate;
    }
    public void setPubdate(String pubdate) {
    this
    .pubdate = pubdate;
    }


    }
    复制代码

    下面就是最最重要的地方了,建立自己的ContentHandler.看下面的代码:

    RSSHandler.java

    复制代码
    package com.sax.org.handler;

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

    import com.sax.org.entity.RSSFeed;
    import com.sax.org.entity.RSSItem;

    public class RSSHandler extends DefaultHandler{
    RSSFeed RssFeed;
    RSSItem RssItem;
    final int RSS_TITLE = 1;
    final int RSS_LINK = 2;
    final int RSS_DESCRIPTION = 3;
    final int RSS_CATEGORY = 4;
    final int RSS_PUBDATE = 5;
    int currentstate = 0;

    public RSSHandler(){}

    public RSSFeed getFeed(){
    return RssFeed;
    }

    @Override
    public void startDocument() throws SAXException {
    // TODO Auto-generated method stub

    RssFeed =
    new RSSFeed();
    RssItem = new RSSItem();
    }

    @Override
    public void endDocument() throws SAXException {
    // TODO Auto-generated method stub


    }

    @Override
    public void startElement(String uri, String localName, String qName,
    Attributes attributes) throws SAXException {
    // TODO Auto-generated method stub

    if(localName.equals("channel")){
    currentstate = 0;
    return;
    }
    if(localName.equals("item")){
    RssItem = new RSSItem();
    return;
    }
    if(localName.equals("title")){
    currentstate = RSS_TITLE;
    return;
    }
    if(localName.equals("description")){
    currentstate = RSS_DESCRIPTION;
    return;
    }
    if(localName.equals("link")){
    currentstate = RSS_LINK;
    return;
    }
    if(localName.equals("category")){
    currentstate = RSS_CATEGORY;
    return;
    }
    if(localName.equals("pubDate")){
    currentstate = RSS_PUBDATE;
    return;
    }
    currentstate = 0;
    }

    @Override
    public void endElement(String uri, String localName, String qName)
    throws SAXException {
    // TODO Auto-generated method stub

    if(localName.equals("item")){
    RssFeed.addItem(RssItem);
    return;
    }
    }

    @Override
    public void characters(char[] ch, int start, int length)
    throws SAXException {
    // TODO Auto-generated method stub

    String theString =
    new String(ch, start, length);
    switch(currentstate){
    case RSS_TITLE:
    RssItem.setTitle(theString);
    currentstate = 0;
    break;
    case RSS_DESCRIPTION:
    RssItem.setDescription(theString);
    currentstate = 0;
    break;
    case RSS_LINK:
    RssItem.setLink(theString);
    currentstate = 0;
    break;
    case RSS_PUBDATE:
    RssItem.setPubdate(theString);
    currentstate = 0;
    break;
    case RSS_CATEGORY:
    RssItem.setCategory(theString);
    currentstate = 0;
    break;
    default:
    return
    ;
    }
    }
    }
    复制代码

    就上面的代码分析,实现一个ContentHandler一般要一下几个步骤:

    1、声明一个类,继承DefaultHandler。DefaultHandler是一个基类,这个类里面简单实现了一个ContentHandler。我们只需要重写里面的方法即可。

    2、重写 startDocument() 和 endDocument(),一般解析将正式解析之前的一些初始化工资放到startDocument()里面,收尾的工作放到endDocument()里面。

    3、重写startElement(),XML解析器遇到XML里面的tag时就会调用这个函数。经常在这个函数内是通过localName俩进行判断而操作一些数据。

    4、重写characters()方法,这是一个回调方法。解析器执行完startElement()后,解析完节点的内容后就会执行这个方法,并且参数ch[]就是节点的内容。这个例子里我们根据currentstate的不同,来判断当前那个tag的内容,并放到合适的实体类中。

    5、重写endElement()方法,这个方法与startElement()相对应,解析完一个tag节点后,执行这个方法。再找个例子中,如果解析一个item结束,就将RSSIiem添加到RSSFeed中。

    最后我们实现一个activity来展现解析的结果:

    复制代码
    package com.sax.org;

    import java.io.IOException;
    import java.net.URL;

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

    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.XMLReader;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.ListView;
    import android.widget.SimpleAdapter;
    import android.widget.TextView;

    import com.sax.org.entity.RSSFeed;
    import com.sax.org.entity.RSSItem;
    import com.sax.org.handler.RSSHandler;

    public class SAXReaderActivity extends Activity {


    public String rssUrl = "http://mc.cz001.com.cn/a/indexconfig/index.rss";
    public RSSFeed feed;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    feed = getFeed(rssUrl);
    showList();
    }

    public RSSFeed getFeed(String rssUrl) {
    try
     {
    
    // 这里我们实现了本地解析,所以注掉了这个取网络数据的。
    // URL url = new URL(rssUrl);

    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser parser = factory.newSAXParser();
    XMLReader reader = parser.getXMLReader();
    RSSHandler handler =
    new RSSHandler();
    reader.setContentHandler(handler);
    InputSource is = new InputSource(this.getClassLoader().getResourceAsStream("rssxml.xml"));//取得本地xml文件
    reader.parse(is);
    return handler.getFeed();
    } catch (ParserConfigurationException e) {
    // TODO Auto-generated catch block

    e.printStackTrace();
    }
    catch (SAXException e) {
    // TODO Auto-generated catch block

    e.printStackTrace();
    }
    catch (IOException e) {
    // TODO Auto-generated catch block

    e.printStackTrace();
    }
    return null;
    }

    public void showList() {
    ListView rsslistview = (ListView) findViewById(R.id.rssList);
    TextView rsstitle = (TextView) findViewById(R.id.rsstitle);
    if (feed == null) {
    rsstitle.setText("访问失败...");
    return;
    }
    SimpleAdapter adapter = new SimpleAdapter(this,
    feed.getAllItemForListView(),
    android.R.layout.simple_list_item_2, new String[] {
    RSSItem.TITLE, RSSItem.PUBDATE }, new int
    [] {
    android.R.id.text1, android.R.id.text2 });
    rsslistview.setAdapter(adapter);
    }
    }
    复制代码

    展示下运行结果:

    OK。

    本文参考自《Google+Android入门与实践》,谢谢。

  • 相关阅读:
    世界上最受欢迎的色彩出炉了,她的名字叫马尔斯绿
    一步一步学会preload和prefetch
    chrome插件编写中需要了解的几个概念和一些方法
    SVG矢量绘图 path路径详解(贝塞尔曲线及平滑)
    为什么Object.prototype在Function的原型链上与Function.prototype在Object的原型链上都为true
    排序算法总结
    iterm2 "agnoster"主题设置中的一些踩坑 2018.8
    webpack4与babel配合使es6代码可运行于低版本浏览器
    认识JWT
    「前端进阶」彻底弄懂前端路由
  • 原文地址:https://www.cnblogs.com/crane13/p/3150187.html
Copyright © 2020-2023  润新知