Android中Xml解析实战

来源:互联网 发布:专业八字算命软件 编辑:程序博客网 时间:2024/06/05 19:42

在Android中,常见的XML解析器分别为DOM解析器、SAX解析器和PULL解析器,下面,我将一一向大家详细介绍。

DOM解析器:

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。

DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档——这就是DOM的工作原理。

DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,这样代码就可以使用DOM接口来操作整个树结构。

由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。 当然,如果XML文件的内容比较小,采用DOM是可行的。

常用的DOM接口和类:

Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。  Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。Node:该接口提供处理并获取节点和子节点值的方法。NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。

下面是DOM的解析流程:
这里写图片描述

  采用DOM解析时具体处理步骤是:1 首先利用DocumentBuilderFactory创建一个DocumentBuilderFactory实例2 然后利用DocumentBuilderFactory创建DocumentBuilder3 然后加载XML文档(Document),4 然后获取文档的根结点(Element),5 然后获取根结点中所有子节点的列表(NodeList),6 然后使用再获取子节点列表中的需要读取的结点。

Xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?><channel><item id="0" url="http://www.baidu.com">百度</item><item id="1" url="http://www.qq.com">腾讯</item><item id="2" url="http://www.sina.com.cn">新浪</item><item id="3" url="http://www.taobao.com">淘宝</item></channel>

Java代码如下:

public class DomPraserDemo extends ListActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        // TODO Auto-generated method stub        super.onCreate(savedInstanceState);        SimpleAdapter adapter = new SimpleAdapter(this, getData(),                R.layout.list, new String[] { "id", "name" }, new int[] {                R.id.textId, R.id.textName });        setListAdapter(adapter);    }    private List<Map<String, String>> getData() {        List<Map<String, String>> list = new ArrayList<Map<String, String>>();        InputStream stream = getResources().openRawResource(R.raw.channels);        List<channel> channlist = getChannelList(stream);        for (int i = 0; i < channlist.size(); i++) {            Map<String, String> map = new HashMap<String, String>();            channel chann = (channel) channlist.get(i);            map.put("id", chann.getId());            map.put("url", chann.getUrl());            map.put("name", chann.getName());            list.add(map);        }        return list;    }    public static List<channel> getChannelList(InputStream stream)    {        List<channel> list=new ArrayList<channel>();        //得到 DocumentBuilderFactory 对象, 由该对象可以得到 DocumentBuilder 对象        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();        try {            //得到DocumentBuilder对象            DocumentBuilder builder=factory.newDocumentBuilder();            //得到代表整个xml的Document对象            Document document=builder.parse(stream);            //得到 "根节点"            Element root=document.getDocumentElement();            //获取根节点的所有items的节点            NodeList items=root.getElementsByTagName("item");            //遍历所有节点            for(int i=0;i<items.getLength();i++)            {                channel chann=new channel();                Element item=(Element)items.item(i);                chann.setId(item.getAttribute("id"));                chann.setUrl(item.getAttribute("url"));                chann.setName(item.getFirstChild().getNodeValue());                list.add(chann);            }        } 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 list;    }}

SAX解析器:

SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。

SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

下面是SAX的解析流程:
这里写图片描述

采用SAX解析时具体处理步骤是:1 创建SAXParserFactory对象2 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器3 根据SAXParser解析器获取事件源对象XMLReader4 实例化一个DefaultHandler对象5 连接事件源对象XMLReader到事件处理类DefaultHandler中6 调用XMLReader的parse方法从输入源中获取到的xml数据7 通过DefaultHandler返回我们需要的数据集合。

解析器主要代码:

    private List<channel> getChannelList() throws ParserConfigurationException, SAXException, IOException    {        //实例化一个SAXParserFactory对象        SAXParserFactory factory=SAXParserFactory.newInstance();        SAXParser parser;        //实例化SAXParser对象,创建XMLReader对象,解析器        parser=factory.newSAXParser();        XMLReader xmlReader=parser.getXMLReader();        //实例化handler,事件处理器        SAXPraserHelper helperHandler=new SAXPraserHelper();        //解析器注册事件        xmlReader.setContentHandler(helperHandler);        //读取文件流        InputStream stream=getResources().openRawResource(R.raw.channels);        InputSource is=new InputSource(stream);        //解析文件        xmlReader.parse(is);        return helperHandler.getList();    }

Handler代码:

public class SAXPraserHelper extends DefaultHandler{    final int ITEM = 0x0005;    List<channel> list;    channel chann;    int currentState = 0;    public List<channel> getList() {        return list;    }    @Override    public void startDocument() throws SAXException {        list = new ArrayList<channel>();    }    @Override    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {        chann = new channel();        if(localName.equals("item")){            for(int i = 0 ;i < attributes.getLength(); i++){                if(attributes.getLocalName(i).equals("id")){                    chann.setId(attributes.getValue(i));                }else if(attributes.getLocalName(i).equals("url")){                    chann.setUrl(attributes.getValue(i));                }            }            currentState = ITEM;            return;        }        currentState = 0;        return;    }    @Override    public void characters(char[] ch, int start, int length) throws SAXException {        if(currentState != 0){            String value = String.valueOf(ch,start,length);            chann.setName(value);            currentState = 0;        }    }    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {        if(localName.equals("item")){            list.add(chann);        }    }}

PULL解析器:

Android并未提供对Java StAX API的支持。但是,Android附带了一个pull解析器,其工作方式类似于StAX。它允许用户的应用程序代码从解析器中获取事件,这与SAX解析器自动将事件推入处理程序相反。

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。

PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。

PULL 的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。

下面是PULL的解析流程:
这里写图片描述

下面是PULL解析XML的过程:读取到xml的声明返回 START_DOCUMENT;读取到xml的结束返回 END_DOCUMENT ;读取到xml的开始标签返回 START_TAG读取到xml的结束标签返回 END_TAG读取到xml的文本返回 TEXT

解析器主要代码:

    private List<Map<String, String>> getData() {        List<Map<String,String>> list =new  ArrayList<Map<String, String>>();        XmlResourceParser xrp = getResources().getXml(R.xml.channels);        try{            while (xrp.getEventType() != XmlPullParser.END_DOCUMENT){                if(xrp.getEventType() == XmlPullParser.START_TAG){                    String tagName = xrp.getName();                    if(tagName.equals("item"))                    {                        Map<String,String> map =new  HashMap<String, String>();                        String id = xrp.getAttributeValue(null,"id");                        map.put("id",id);                        String url = xrp.getAttributeName(1);                        map.put("url", url);                        String name = xrp.nextText();                        map.put("name", name);                        list.add(map);                    }                }                xrp.next();            }        }catch (XmlPullParserException e) {            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        }        return list;    }

SAX、DOM、PULL的比较

  1. 内存占用
    这是一个根本性问题。由于Android手机性能相对于现在的应用操作还是有限的,程序对内存的占用直接影响到了解析XML的速度。在这点上,SAX、Pull以它们比DOM占用更少的内存的解析方式,更适合于Android手机开发。
  2. 编程方式
    SAX采用事件驱动,在相应事件触发的时候,会调用用户编写好的方法。也就是说,每解析一类XML,就要编写一个新的适合该类XML的处理类。这显然不是一个好的解决办法,尽管其在解析速度上是那么优秀。而这点,DOM因为是W3C的规范。所以被更多程序员所知道和使用。所以在开发过程中,没有太大困难。Pull虽然属于一个小众的,甚至是不为人知的解析器,但是通过上面对其介绍和示例,我们应该能看出它的简洁性。
  3. 访问与修改
    由于采用的是流式解析,这就说明它们不能像DOM那样随机访问,XML的其中任意一个节点。并且,SAX并没有提供对文档中加节点的API,更没有删除,修改文档内容的方法。
  4. 访问方式
    这是产生它们解析快慢的根本原因。如果把SAX和Pull比喻成一目十行,很快但是是走马观花的阅读方式的话,那么DOM就是逐字逐句的阅读,很慢,但是是过目不忘。这里还要需要注意的是,SAX,Pull解析的方式是同步的,即解析器读到哪里,就对哪里进行处理。而DOM是已经将文件解析好后,供用户提取XML中感兴趣的信息。

总结:

出于对内存占用的考虑,推荐使用SAX或者Pull来工作。可是根据它们工作的原理:如果只是需要XML最后的几个节点的相关信息,或者出现反复检索XML文件的情况。那么基本上三者在性能上就没有什么差异,反而在这时,SAX的处理类会使程序显得比其他的实现方式显得臃肿。所以,想做一个高性能的Android软件,还是要多分析,选择合适的工具,才能发挥它的作用。

源码

转自此处

0 0
原创粉丝点击