android基础知识09:xml文件解析01 SAX

       本文主要讲述android中xml的解析方式。

       android基础知识09:xml文件解析01 SAX

        android基础知识09:xml文件解析02 DOM

       android基础知识09:xml文件解析03 PULL


       主要参考了《android解析xml文件的方式(其一)》《Android XML解析

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

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

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

      

     其中相关文件定义为:

   river.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>


    main.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="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

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;
    }
}

       第一部分先来介绍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个接口的连接,详细介绍请见下表:


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


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

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对象中对每一个元素节点,属性,文本内容,文档内容进行处理。
        前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。
         根据以上的解释,我们可以得出以下处理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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值