Android XML文档解析(一)——SAX解析
------------------------------------------------------------------------------------------------------
此文章仅作为学习交流所用
转载或引用请务必注明原文地址:
http://blog.csdn.net/luzhenrong45/article/details/11851607
或联系作者:[email protected]
谢谢!
------------------------------------------------------------------------------------------------------
一. XML介绍
XML:extensible markup language,可扩展标记语言. 与HTML(超文本标记语言,即Hypertext Markup Language)一样,XML也是 标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML与HTML的设计区别是:XML 被设计为传输和存储数据,其焦点是数据的内容。而HTML 被设计用来显示数据,其焦点是数据的外观。HTML 旨在显示信息,而 XML 旨在传输信息。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。其目的是为了促进Internet上结构化文档的交换。简单的说,XML是一组规则和准则的集合,用于以无格式文本来描述结构化数据.
XML是纯数据描述,与编程语言、操作系统或传输协议无关,从而将数据从以代码为中心的基础结构所产生的约束中解放出来,让数据能够在Web上更自由的流通。然而XML本身只是以纯文本对数据进行编码的一种格式,要想利用XML,或者说利用XML文件中所编码的数据,必须先将数据从纯文本中解析出来,因此,必须有一个能够识别XML文档中信息的解析器,用来解释XML文档并提取其中的数据。根据数据提取的不同需求,又存在着多种解析方式,不同的解析方式有着各自的优缺点和适用环境。选择合适的XML解析技术能够有效提升应用系统的整体性能.在Android中,常见的XML解析器分别为SAX解析器、DOM解析器和PULL解析器.
下面,介绍SAX解析方式.
二. SAX解析
SAX,全称Simple API for XML,既是指一种接口,也是指一个软件包。SAX解析XML文件采用事件驱动的方式进行,也就是说,SAX是逐行扫描文件,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时回调你写好的事件处理程序,然后继续同样的扫描,直至文档结束。使用SAX的优势在于其解析速度较快,占用内存较少(相对于DOM而言)。而且SAX在解析文件的过程中得到自己需要的信息后可以随时终止解析,并不一定要等文件全部解析完毕。凡事有利必有弊,其劣势在于SAX采用的是流式处理方式,当遇到某个标签的时候,它并不会记录下以前所遇到的标签,也就是说,在处理某个标签的时候,比如在 startElement方法中,所能够得到的信息就是标签的名字和属性,至于标签内部的嵌套结构,上层标签、下层标签以及其兄弟节点的名称等等与其结构相关的信息都是不得而知的。实际上就是把XML文件的结构信息丢掉了,如果需要得到这些信息的话,只能你自己在程序里进行处理了。所以相对DOM而言,SAX处理XML文档没有DOM方便,SAX处理的过程相对DOM而言也比较复杂。
//用于处理文档解析开始事件 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 //当文档结束的时候,调用这个方法,可以在其中做一些善后的工作 public void endDocument() throws SAXException
三. SAX解析XML步骤
Android 使用SAX解析XML文件有两种方式,第一是直接使用SAXParser,或者创建一个XMLReader.不管采用哪种方式,前面三个步骤是一样的.
(一)第一步:加载需要解析的文件,将一个xml文档或者资源变成一个java可以处理的InputStream流.比如我的xml文件名是laolu_xml.xml,放在src目录下,可通过类装载器的方式获得文件路径,再获得文件的输入流
InputStream is= this.getClass().getClassLoader().getResourceAsStream("laolu_xml.xml");
(二)第二步:新建一个工厂类SAXParserFactory
SAXParserFactory factory = SAXParserFactory.newInstance();
(三)第三步:让工厂类产生一个SAX的解析类SAXParser
SAXParser saxParser = factory.newSAXParser();
(四)第四步:可以采用XMLReader解析或直接使用SAXParser解析.
(1)采用XMLReader
1) 从SAXPsrser中得到一个XMLReader实例 XMLReader xmlReader = saxParser.getXMLReader(); 2) 把自己写的handler注册到XMLReader中,一般最重要的就是ContentHandler: SAXForHandler handler = new SAXForHandler (); //SAXForHandler为自己写的SAX解析类 xmlReader.setContentHandler(handler); 3) 开始解析XML xmlReader.parse(new InputSource(is));
(2)直接使用SAXParser解析
SAXForHandler handler = new SAXForHandler (); saxParser.parse(is,handler);
两种方式都可以对XML文件进行解析,使用的效果是完全一样的.其实SAXParser是JAXP(JAVA API for XML Processing) 对XMLReader的一个封装,只不过,SAXParser能接受更多类型的参数,能方便对不同数据源的XML文档进行解析,使用XMLReader要稍微烦琐一些.
四.SAX解析实例.
(一)下面以一个工程实例来实现SAX对XML文件的解析.新建一个xml文件: laolu_xml.xml,该文件放在工程根目录下面.内容如下:
<?xml version="1.0" encoding="UTF-8"?> <persons> <person id="0"> <name>刘德华</name> <sex>男</sex> <age>21</age> </person> <person id="1"> <name>张漫玉</name> <sex>女</sex> <age>22</age> </person> <person id="2"> <name>张易谋</name> <sex>男</sex> <age>23</age> </person> <person id="3"> <name>周新驰</name> <sex>未知</sex> <age>24</age> </person> <person id="4"> <name>纳兰容若</name> <sex>男</sex> <age>25</age> </person> </persons>
(二)先看一下实际效果图,Android对于XML解析,主要有sax,dom,pull三种方式,这里只介绍sax方式.其他两种以后再介绍.
(三)工程源码框架
(1)布局文件:
main_xml.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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".XML_ParseActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <Button android:id="@+id/sax_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/sax_parse"/> <Button android:id="@+id/dom_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/dom_parse" /> <Button android:id="@+id/pull_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/pull_parse" /> <ListView android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
listview_xml.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <View android:layout_width="0.5px" android:layout_height="fill_parent" android:background="#B8B8B8" android:visibility="visible" /> <TextView android:id="@+id/id" android:layout_width="60dip" android:layout_height="40dip" /> <View android:layout_width="0.5px" android:layout_height="fill_parent" android:background="#B8B8B8" android:visibility="visible" /> <TextView android:id="@+id/name" android:layout_width="120dip" android:layout_height="40dip" /> <View android:layout_width="0.5px" android:layout_height="fill_parent" android:background="#B8B8B8" android:visibility="visible" /> <TextView android:id="@+id/sex" android:layout_width="60dip" android:layout_height="40dip" /> <View android:layout_width="0.5px" android:layout_height="fill_parent" android:background="#B8B8B8" android:visibility="visible" /> <TextView android:id="@+id/age" android:layout_width="120dip" android:layout_height="40dip" /> </LinearLayout>
(2)这里重写了SimpleAdapter适配器,使用ListView的效果更好看一点.当然,这一点跟今天的主题没关.
MySimpleAdapter.java
package com.laolu.adapter; import java.util.List; import java.util.Map; import android.content.Context; import android.graphics.Color; import android.view.View; import android.view.ViewGroup; import android.widget.SimpleAdapter; public class MySimpleAdapter extends SimpleAdapter { public MySimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view=null; if(convertView!=null) { view=convertView; }else { view=super.getView(position,convertView,parent); } int colors[]={Color.GRAY,Color.WHITE}; view.setBackgroundColor(colors[position%2]); // 每个item之间颜色不同 return super.getView(position, view, parent);//返回自己的view } }
(3)自己实现的SAX解析类SAXForHandler,继承DefaultHandler,而DefaultHandler是实现了ContenHandler的接口的.ContentHandler提供了之前所说的相应的事件方法,所以这个类是最重要的.因为这些回调函数都是自己写代码实现的.
SAXForHandler.java
package com.laolu.parser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import android.util.Log; public class SAXForHandler extends DefaultHandler { private static final String TAG="SAXForHandler"; private List<Map<String,Object>> persons; private Map<String,Object> person; private String nowWitch; public List<Map<String,Object>> getPersons() { return persons; } @Override public void startDocument() throws SAXException { persons=new ArrayList<Map<String,Object>>(); Log.i(TAG,"***startDocument()***"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if("person".equals(localName)) { for(int i=0;i<attributes.getLength();i++) { Log.i(TAG,"arrtributes: "+attributes.getLocalName(i)+"_attributes_Value" +attributes.getValue(i)); person=new HashMap<String,Object>(); person.put("id", attributes.getValue(i)); } } nowWitch=localName; Log.i(TAG, qName+"***startElement()***"); } @Override public void characters(char[] ch, int start, int length) throws SAXException { String data=new String(ch,start,length).trim(); if(!"".equals(data.trim())) { Log.i(TAG, "content: "+data.trim()); } if("name".equals(nowWitch)) { person.put("name", data.trim()); }else if("age".equals(nowWitch)) { person.put("age", data.trim()); }else if("sex".equals(nowWitch)) { person.put("sex", data.trim()); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { Log.i(TAG,qName+"***endElement()***"); if("person".equals(localName)) { persons.add(person); person=null; } nowWitch=null; } @Override public void endDocument() throws SAXException { Log.i(TAG, "***endDocument()***"); } }
(4)主Activity,按下按键,ListView显示解析结果.
package com.laolu.main; import java.io.InputStream; import java.util.List; import java.util.Map; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import com.example.xml.R; import com.laolu.adapter.MySimpleAdapter; import com.laolu.parser.SAXForHandler; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; public class XML_ParseActivity extends Activity { private static final String SAX="SAX_Parse"; private static final String DOM="DOM_Parse"; private static final String PULL="Pull_Parse"; ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_xml); listView=(ListView) findViewById(R.id.listview); Button saxButton=(Button) findViewById(R.id.sax_btn); Button domButton=(Button) findViewById(R.id.dom_btn); Button pullButton=(Button) findViewById(R.id.pull_btn); saxButton.setOnClickListener(new MyListener()); domButton.setOnClickListener(new MyListener()); pullButton.setOnClickListener(new MyListener()); } public class MyListener implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.sax_btn: try{ updateListViewBySAX(); }catch(Exception e) { Log.i(SAX,"SAX Exception!!!"); } break; case R.id.dom_btn: break; case R.id.pull_btn: break; } } } public void updateListViewBySAX() throws Exception { InputStream inputStream = this.getClass().getClassLoader(). getResourceAsStream("laolu_xml.xml"); SAXForHandler saxForHandler = new SAXForHandler(); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); saxParser.parse(inputStream, saxForHandler);//采用 List<Map<String,Object>> persons = saxForHandler.getPersons(); SimpleAdapter simpleAdapter=new MySimpleAdapter(this, persons, R.layout.listview_xml, new String[]{"id","name","sex","age"}, new int[]{R.id.id,R.id.name,R.id.sex,R.id.age}); listView.setAdapter(simpleAdapter); inputStream.close(); } }