android基礎知識09:xml檔案解析01 SAX

來源:互聯網
上載者:User

       本文主要講述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.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.