在androd手机中处理xml数据时很常见的事情,通常在不同平台传输数据的时候,我们就可能使用xml,xml是与平台无关的特性,被广泛运用于数据通信中,那么在android中如何解析xml文件数据呢?

通常有三种方式:DOM,SAX,PULL。Pull为Android内置的XML文件解析器。

为了讲解xml文件解析,我这里做了一个android项目,其内容如下:


android xml文件透明 安卓xml文件怎么打开_android xml文件透明

其中相关文件定义为:

river.xml

main.xml

<?xml version="1.0" encoding="utf-8"?> <rivers> <river name="灵渠" length="605"> <introduction> 灵渠在广西壮族自治区兴安县境内,是世界上最古老的运河之一,有着“世界古代水利建筑明珠”的美誉。灵渠古称秦凿渠、零渠、陡河、兴安运河,于公元前214年凿成通航,距今已2217年,仍然发挥着功用。 </introduction> <imageurl> http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg </imageurl> </river> <river name="胶莱运河" length="200"> <introduction> 胶莱运河南起黄海灵山海口,北抵渤海三山岛,流经现胶南、胶州、平度、高密、昌邑和莱州等,全长200公里,流域面积达5400平方公里,南北贯穿山东半岛,沟通黄渤两海。胶莱运河自平度姚家村东的分水岭南北分流。南流由麻湾口入胶州湾,为南胶莱河,长30公里。北流由海仓口入莱州湾,为北胶莱河,长100余公里。 </introduction> <imageurl> http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg </imageurl> </river> <river name="苏北灌溉总渠" length="168"> <introduction> 位于淮河下游江苏省北部,西起洪泽湖边的高良涧,流经洪泽,青浦、淮安,阜宁、射阳,滨海等六县(区),东至扁担港口入海的大型人工河道。全长168km。 </introduction> <imageurl> http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg </imageurl> </river> </rivers>

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/tv_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/btn_dom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="dom" /> <Button android:id="@+id/btn_sax" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="sax" /> <Button android:id="@+id/btn_pull" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="pull" /> <ListView android:id="@+id/lv_riverlist" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>riveritem.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:orientation="horizontal"> <TextView android:id="@+id/tv_name" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_length" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> <TextView android:id="@+id/tv_introduction" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>XmlTestActivity.java 
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/tv_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/btn_dom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="dom" /> <Button android:id="@+id/btn_sax" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="sax" /> <Button android:id="@+id/btn_pull" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="pull" /> <ListView android:id="@+id/lv_riverlist" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>riveritem.xml

public class XmlTestActivity extends Activity { /** Called when the activity is first created. */ private Button btn_dom; private Button btn_sax; private Button btn_pull; private TextView tv_type; private RiverAdapater riverAdapter; private List<River> rivers; private String fileName="river.xml"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btn_dom=(Button)findViewById(R.id.btn_dom); btn_sax=(Button)findViewById(R.id.btn_sax); btn_pull=(Button)findViewById(R.id.btn_pull); tv_type=(TextView)findViewById(R.id.tv_type); ListView listView =(ListView)findViewById(R.id.lv_riverlist); riverAdapter = new RiverAdapater(); listView.setAdapter(riverAdapter); btn_dom.setOnClickListener(new OnClickListener() { public void onClick(View v) { DomXml domXml=new DomXml(); rivers=domXml.getRiversFromXml(fileName, XmlTestActivity.this.getApplicationContext()); riverAdapter.setRiverList(rivers); tv_type.setText("dom"+String.valueOf(rivers.size())); } }); btn_sax.setOnClickListener(new OnClickListener() { public void onClick(View v) { DomXml domXml=new DomXml(); rivers=domXml.getRiversFromXml(fileName, XmlTestActivity.this.getApplicationContext()); riverAdapter.setRiverList(rivers); tv_type.setText("sax"+String.valueOf(rivers.size())); } }); btn_pull.setOnClickListener(new OnClickListener() { public void onClick(View v) { DomXml domXml=new DomXml(); rivers=domXml.getRiversFromXml(fileName, XmlTestActivity.this.getApplicationContext()); riverAdapter.setRiverList(rivers); tv_type.setText("pull"+String.valueOf(rivers.size())); } }); } }River.java

public class River implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getIntroduction() { return introduction; } public void setIntroduction(String introduction) { this.introduction = introduction; } public String getImageurl() { return imageurl; } public void setImageurl(String imageurl) { this.imageurl = imageurl; } private int length; private String introduction; private String imageurl; }RiverAdapater.java
public class RiverAdapater extends BaseAdapter{ private List<River> riverList = Collections.emptyList(); public int getCount() { return riverList.size(); } public Object getItem(int position) { return riverList.get(position); } public long getItemId(int position) { return position; } public void setRiverList(List<River> riverList) { this.riverList = riverList; notifyDataSetInvalidated(); } public class RiverHolder { public TextView rname; public TextView rlength; public TextView rintroduction; } public View getView(int position, View convertView, ViewGroup parent) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.riveritem, null); RiverHolder wh = new RiverHolder(); wh.rname = (TextView) convertView.findViewById(R.id.tv_name); wh.rlength = (TextView) convertView.findViewById(R.id.tv_length); wh.rintroduction = (TextView) convertView.findViewById(R.id.tv_introduction); River wb = riverList.get(position); if(wb!=null){ wh.rname.setText(wb.getName()); wh.rlength.setText(String.valueOf(wb.getLength())); wh.rintroduction.setText(wb.getIntroduction(), TextView.BufferType.SPANNABLE); } return convertView; } } 
public class River implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getIntroduction() { return introduction; } public void setIntroduction(String introduction) { this.introduction = introduction; } public String getImageurl() { return imageurl; } public void setImageurl(String imageurl) { this.imageurl = imageurl; } private int length; private String introduction; private String imageurl; }RiverAdapater.java

第一部分先来介绍SAX解析方式:

SAX即是:Simple API for XML
SAX是基于事件驱动的。当然android的事件机制是基于回调函数的,在用SAX解析xml文档时候,在读取到文档开始和结束标签时候就会回调一个事件,在读取到其他节点与内容时候也会回调一个事件。
既然涉及到事件,就有事件源,事件处理器。在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口
XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接,详细介绍请见下表:

android xml文件透明 安卓xml文件怎么打开_android_02

但是我们无需都继承这4个接口,SDK为我们提供了DefaultHandler类来处理,DefaultHandler类的一些主要事件回调方法如下:

android xml文件透明 安卓xml文件怎么打开_java_03

由以上可知,我们需要XmlReader 以及DefaultHandler来配合解析xml。
处理思路是:
1:创建SAXParserFactory对象
2: 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器
3:根据SAXParser解析器获取事件源对象XMLReader
4:实例化一个DefaultHandler对象
5:连接事件源对象XMLReader到事件处理类DefaultHandler中
6:调用XMLReader的parse方法从输入源中获取到的xml数据
7:通过DefaultHandler返回我们需要的数据集合。
代码如下:

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

public class SaxXml { public List<River> parse(String xmlPath,Context context){ List<River> rivers=null; SAXParserFactory factory=SAXParserFactory.newInstance(); try { SAXParser parser=factory.newSAXParser(); //获取事件源 XMLReader xmlReader=parser.getXMLReader(); //设置处理器 RiverHandler handler=new RiverHandler(); xmlReader.setContentHandler(handler); //解析xml文档 //xmlReader.parse(new InputSource(new URL(xmlPath).openStream())); xmlReader.parse(new InputSource(context.getAssets().open(xmlPath))); rivers=handler.getRivers(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return rivers; } public class RiverHandler extends DefaultHandler{ private boolean isRiver; private boolean xintroduction; private boolean ximageurl; private River river; private List<River> rivers = null; public List<River> getRivers() { return rivers; } /* * 接收文档的开始的通知。 */ @Override public void startDocument() throws SAXException { rivers = new ArrayList<River>(); } /**导航到开始标签触发**/ public void startElement (String uri, String localName, String qName, Attributes attributes){ String tagName=localName.length()!=0?localName:qName; tagName=tagName.toLowerCase().trim(); //如果读取的是river标签开始,则实例化River if(tagName.equals(Contant.RIVER)){ isRiver=true; river=new River(); /**导航到river开始节点后**/ river.setName(attributes.getValue(Contant.NAME)); river.setLength(Integer.parseInt(attributes.getValue(Contant.LENGTH))); } //然后读取其他节点 if(isRiver){ if(tagName.equals(Contant.INTRODUCTION)){ xintroduction=true; }else if(tagName.equals(Contant.IMAGEURL)){ ximageurl=true; } } } /**导航到结束标签触发**/ public void endElement (String uri, String localName, String qName){ String tagName=localName.length()!=0?localName:qName; tagName=tagName.toLowerCase().trim(); //如果读取的是river标签结束,则把River添加进集合中 if(tagName.equals(Contant.RIVER)){ isRiver=true; rivers.add(river); } //然后读取其他节点 if(isRiver){ if(tagName.equals(Contant.INTRODUCTION)){ xintroduction=false; }else if(tagName.equals(Contant.IMAGEURL)){ ximageurl=false; } } } //这里是读取到节点内容时候回调 public void characters (char[] ch, int start, int length){ //设置属性值 if(xintroduction){ //解决null问题 river.setIntroduction(river.getIntroduction()==null?"":river.getIntroduction()+new String(ch,start,length)); }else if(ximageurl){ //解决null问题 river.setImageurl(river.getImageurl()==null?"":river.getImageurl()+new String(ch,start,length)); } } } } 重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。


根据以上的解释,我们可以得出以下处理xml文档逻辑:


1:当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8等等。


2:当导航到rivers开始标签时,在回调方法startElement中可以实例化一个集合用来存贮list,不过我们这里不用,因为在构造函数中已经实例化了。


3:导航到river开始标签时,就说明需要实例化River对象了,当然river标签中还有name ,length属性,因此实例化River后还必须取出属性值,attributes.getValue(NAME),同时赋予river对象中,同时添加为导航到的river标签添加一个boolean为真的标识,用来说明导航到了river元素。


4:当然有river标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。因此


5:它还会导航到文本内标签,(就是<img></img>里面的内容),回调方法characters,我们一般在这个方法中取出就是<img></img>里面的内容,并保存。


6:当然它是一定会导航到结束标签</river> 或者</rivers>的,如果是</river>标签,记得把river对象添加进list中。如果是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false.