Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Xml 格式數據的生成和解析

Xml 格式數據的生成和解析

編輯:關於Android編程

什麼是XML

XML全稱為Extensible Markup Language, 意思是可擴展的標記語言,它是 SGML(標准通用標記語言)的一個子集。

XML語法上和HTML比較相似,但HTML中的元素是固定的,而XML的標簽是可以由用戶自定義的。

W3C在1998年2月發布1.0版本;
W3C在2004年2月發布1.1版本,但因為1.1版本不能向下兼容1.0版本,所以1.1沒有人用。同時,在2004年2月W3C又發布了1.0版本的第三版。我們要學習的還是1.0版本!!!

W3C組織

W3C是萬維網聯盟(World Wide Web Consortium)英文的縮寫,它成立於1994年10月,以開放論壇的方式來促進開發互通技術(包括規格、指南、軟件和工具),開發網絡的全部潛能。萬維網聯盟(W3C)從1994年成立以來,已發布了90多份Web技術規范,領導著Web技術向前發展。

W3C認為自身不是官方組織,因此將它正式發布的規范稱為推薦(建議)標准,意思是進一步標准化的建議,但是由於組織自身的權威性往往成為事實上的標准。

XML的作用

程序的配置文件(這也是最後大家使用XML最常見的目的); 數據交換:不同語言之間用來交換數據; 小型數據庫:用來當數據庫存儲數據。

XML與HTML比較

HTML的元素都是固定的,而XML可以自定義元素; HTML用浏覽器來解析執行, XML的解析器通常需要自己來寫(因為元素是自定義的); HTML只能用來表示網頁,而XML可以做的事情很多。

XML和properties(屬性文件)比較

屬性文件只能存儲平面信息,而XML可以存儲結構化信息; 解析屬性文件只需要使用Properties類就可以了,而解析XML文檔是很復雜的。

XML文檔的組成部分

XML文檔聲明;重要 XML處理指令;看完了,就可以忘了! XML元素;最重要 XML特殊字符和CDATA區;一看就會 XML注釋。不看都會


    
        zhangSan
        23
        male
    
    
        liSi
        32
        female
    
    
        wangWu
        55
        male
    

什麼是xml文檔聲明

可以把xml文檔聲明看成是xml文檔說明。
最簡單的xml文檔聲明:< ?xml version=”1.0”? >
注意,XML是區別大小寫,這一點不同與HTML!

xml文檔聲明結構

version屬性
用於說明當前xml文檔的版本,因為都是在用1.0,所以這個屬性值大家都寫1.0,version屬性是必須的;

encoding屬性
用於說明當前xml文檔使用的字符編碼集,xml解析器會使用這個編碼來解析xml文檔。encoding屬性是可選的,默認為UTF-8。注意,如果當前xml文檔使用的字符編碼集是gb2312,而encoding屬性的值為UTF-8,那麼一定會出錯的;

standalone屬性
用於說明當前xml文檔是否為獨立文檔,如果該屬性值為yes,表示當前xml文檔是獨立的,如果為no表示當前xml文檔不是獨立的,即依賴外部的文件。默認是yes

沒有xml文檔聲明的xml文檔,不是格式良好的xml文檔;

xml文檔聲明必須從xml文檔的1行1列開始。

轉義字符

因為在xml文檔中有些字符是特殊的,不能使用它們作為文本數據。例如:不能使用“<”或“>”等字符作為文本數據,所以需要使用轉義字符來表示。
例如,你可能會說,其中第二個是a元素的文本內容,而不是一個元素的開始標簽,但xml解析器是不會明白你的意思的。
把修飾為<a>,這就OK了。

這裡寫圖片描述

轉義字符都是以“&”開頭,以“;”結束。這與後面我們學習的實體是相同的。

CDATA區(CDATA段)

當大量的轉義字符出現在xml文檔中時,會使xml文檔的可讀性大幅度降低。這時如果使用CDATA段就會好一些。

在CDATA段中出現的“<”、“>”、“””、“’”、“&”,都無需使用轉義字符。這可以提高xml文檔的可讀性

<a><![CDATA[<a>]]></a>

在CDATA段中不能包含“]]>”,即CDATA段的結束定界符

XML實戰案例

使用xml 作為數據交互的載體是Android 中非常重要的功能,比如天氣預報數據、短信備份數據、通訊錄數據都可以以xml 的格式通過網絡傳輸。

為了演示Xml 數據的操作,我模擬了一個短信備份的案例。

需求:界面如圖1-10 所示。上面是三個Button,前兩個分別對應兩種不同方式生成xml,第三個Button點擊後解析備份的xml 文件,然後將數據展現在下面的ScrollView 中。短信數據是模擬的假數據。

這裡寫圖片描述

生成的xml 格式如下


    
      5554我是內容<>0
      
      5555我是內容<>1
        
 

編寫布局文件


拼接字符串方式生成Xml 文件

        //第一種方式生成xml
        public void click1 (View view)throws Exception {
            StringBuilder sb = new StringBuilder();
            sb.append("");
            sb.append("");
            for (int i = 0; i < 50; i++) {
                sb.append("");
                sb.append("");
                sb.append(5554 + i);
                sb.append("");
                sb.append("");
                sb.append("我是短信內容" + i);
                sb.append("");
                sb.append("");
                sb.append("");
            }
            sb.append("");

            //使用系統提供的方法獲取文件輸出流
            FileOutputStream fos = this.openFileOutput("info.xml", MODE_PRIVATE);
            fos.write(sb.toString().getBytes());
            fos.close();
        }

上面的代碼雖然也可以生成xml 文件,但是無法對特殊字符進行處理,比如如果短信內容包含“

使用XmlSerializer 生成Xml 文件

        /**
         * 第二種方式生成xml
         * 使用Android 提供的API
         */
        public void click2 (View view)throws Exception {
            //在data 目錄中創建info2.xml 文件,獲取輸出流
            FileOutputStream fos = openFileOutput("info2.xml", MODE_PRIVATE);
            //通過Xml 類創建一個Xml 序列化器
            XmlSerializer serializer = Xml.newSerializer();
            //給序列化器設置輸出流和輸出流編碼
            serializer.setOutput(fos, "utf-8");
            /**
             * 讓序列化器開發寫入xml 的頭信息,其本質是寫入內容:
             * ""
             */
            serializer.startDocument("utf-8", true);
            /**
             * 開始寫入smses 標簽,
             * 第一個參數是該標簽的命名空間, 這裡不需要
             */
            serializer.startTag(null, "smses");
            for (int i = 0; i < 50; i++) {
                //開始sms 標簽
                serializer.startTag(null, "sms");
                //開始address 標簽
                serializer.startTag(null, "address");
                //在address 標簽中間寫入文本
                serializer.text("" + (5554 + i));
                //結束address 標簽
                serializer.endTag(null, "address");
                //開始body 標簽
                serializer.startTag(null, "body");
                //在body 標簽中間寫入文本
                serializer.text("我是內容<>" + i);
                //結束body 標簽
                serializer.endTag(null, "body");
                serializer.startTag(null, "time");
                serializer.text(new Date().getTime() + "");
                serializer.endTag(null, "time");
                //結束sms 標簽
                serializer.endTag(null, "sms");
            }
            //結束smses 標簽
            serializer.endTag(null, "smses");
            //結束文檔
            serializer.endDocument();
            fos.close();
        }

使用XmlSerializer 生成xml 文件是推薦的方式,因為該api 內部已經實現了對特殊字符的處理

使用Pull 解析Xml 格式數據

asserts 資源目錄中的文件只能讀不能寫,多用於隨apk 一起發布的固定不變的數據,比如可以將國內所有的城市列表放在裡面。

這裡將生成的info2.xml 放到asserts 目錄中,然後讀取、解析、展現

    //使用pull 解析xml 數據
    public void click3(View v) throws Exception {
        /**
         * 將解析出來的數據封裝在Sms 中,然後保存到ArrayList 中
         * Sms 是自定義的一個JavaBean
         */
        ArrayList smses = null;
        Sms sms = null;
        //調用父類提供的getAssets()方法獲取AssertManager 對象
        AssetManager assetManager = getAssets();
        //使用assetManager 的open 方法直接獲取輸入流對象
        InputStream inputStream = assetManager.open("info2.xml");
        //通過Xml 的靜態方法獲取Xml 解析器
        XmlPullParser parser = Xml.newPullParser();
        //設置輸入流和編碼
        parser.setInput(inputStream, "utf-8");
        //獲取事件類型
        int event = parser.next();
        //如果沒有解析到文檔的結尾,則循環解析
        while (event != XmlPullParser.END_DOCUMENT) {
            //獲取當前解析到的標簽名稱
            String tagName = parser.getName();
            //如果是“開始標簽”事件
            if (event == XmlPullParser.START_TAG) {
                //判斷當前解析到的開始標簽是哪個
                if ("smses".equals(tagName)) {
                    smses = new ArrayList();
                } else if ("sms".equals(tagName)) {
                    sms = new Sms();
                } else if ("address".equals(tagName)) {
                    sms.setAddress(parser.nextText());
                } else if ("body".equals(tagName)) {
                    sms.setBody(parser.nextText());
                } else if ("time".equals(tagName)) {
                    sms.setTime(parser.nextText());
                }
                //如果是“結束標簽”事件
            } else if (event == XmlPullParser.END_TAG) {
                if ("sms".equals(tagName)) {
                    //如果是sms 結尾,則將創建的sms 對象添加到集合中
                    smses.add(sms);
                }
            }
            //繼續獲取下一個事件
            event = parser.next();
        }
        inputStream.close();
        //將數據展示到界面
        showSms(smses);

    }

    /**
     * 將短信顯示到TextView 中
     */
    private void showSms(ArrayList smses) {
        StringBuilder sb = new StringBuilder();
        for (Sms s : smses) {
            sb.append(s.toString() + "\n");
        }
        tv_sms.setText(sb.toString());
    }

Pull 解析和SAX 解析對比

Pull 解析器的運行方式與SAX 解析器相似,都屬於事件驅動模式。它提供了類似的事件,如:開始元素和結束元素事件,使用parser.next()可以進入下一個元素並觸發相應事件。事件將作為數值代碼被發送,因此可以使用一個switch 對感興趣的事件進行處理。當元素開始解析時,調用parser.nextText()方法可以獲
取下一個Text 類型元素的值。

SAX 解析器的工作方式是自動將事件推入事件處理器進行處理,因此你不能控制事件的處理主動結束;而Pull 解析器的工作方式為允許你的應用程序代碼主動從解析器中獲取事件,正因為是主動獲取事件,因此可以在滿足了需要的條件後不再獲取事件,結束解析

簡單新聞客戶端

這裡寫圖片描述

新聞數據



    
        熱烈祝賀黑馬52期平均薪水突破13k
        15687
        http://192.168.1.100:8080/images/6.jpg
    
    
        凶手是死者同事,維護死者代碼時完全看不懂而痛下殺手
        16359
        http://192.168.1.100:8080/images/7.jpg
    
    
        凶手是一名程序員,因死者對項目需求頻繁改動而痛下殺手
        14112
        http://192.168.1.100:8080/images/7.jpg
    
    
        最高法駁回360上訴, 維持一審宣判.
        6427
        http://192.168.1.100:8080/images/1.jpg
    
    
        市民: 因霧霾起訴環保局; 公務員談"緊日子": 堅決不出去.
        681
        http://192.168.1.100:8080/images/2.jpg
    
    
        外文局: 國際民眾認可中國大國地位;法院: "流量清零"未侵權.
        1359
        http://192.168.1.100:8080/images/3.jpg
    
    
        放假時我醒了不代表我起床了, 如今我起床了不代表我醒了!
        11616
        http://192.168.1.100:8080/images/4.jpg
    
    
        "媽, 我在東莞被抓, 要2萬保釋金, 快匯錢到xxx!"
        10339
        http://192.168.1.100:8080/images/5.jpg
    
    
        少壯不努力,老大做IT
        14612
        http://192.168.1.100:8080/images/8.jpg
    
    
        問君能有幾多愁,恰似調完代碼改需求
        13230
        http://192.168.1.100:8080/images/8.jpg
    
    
        覺得我帥的人工資一般都比較高
        9928
        http://192.168.1.100:8080/images/8.jpg
    
    
        市民: 因霧霾起訴環保局; 公務員談"緊日子": 堅決不出去.
        681
        http://192.168.1.100:8080/images/2.jpg
    
    
        外文局: 國際民眾認可中國大國地位;法院: "流量清零"未侵權.
        1359
        http://192.168.1.100:8080/images/3.jpg
    

布局文件



    

ListView的item布局




    
    
     
      

實體bean

public class News {

    private String title;
    private String detail;
    private String comment;
    private String imageUrl;

    @Override
    public String toString() {
        return "News [title=" + title + ", detail=" + detail + ", comment="
                + comment + ", imageUrl=" + imageUrl + "]";
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

實現代碼

public class MainActivity extends Activity {

    List newsList;
    Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            ListView lv = (ListView) findViewById(R.id.lv);
            lv.setAdapter(new MyAdapter());
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getNewsInfo();
//      ListView lv = (ListView) findViewById(R.id.lv);
//      //要保證在設置適配器時,新聞xml文件已經解析完畢了
//      lv.setAdapter(new MyAdapter());
    }

    class MyAdapter extends BaseAdapter{

        //得到模型層中元素的數量,用來確定listview需要有多少個條目
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return newsList.size();
        }
        @Override
        //返回一個View對象,作為listview的條目顯示至界面
        public View getView(int position, View convertView, ViewGroup parent) {

            News news = newsList.get(position);
            View v = null;
            ViewHolder mHolder;
            if(convertView == null){
                v = View.inflate(MainActivity.this, R.layout.item_listview, null);
                mHolder = new ViewHolder();
                //把布局文件中所有組件的對象封裝至ViewHolder對象中
                mHolder.tv_title = (TextView) v.findViewById(R.id.tv_title);
                mHolder.tv_detail = (TextView) v.findViewById(R.id.tv_detail);
                mHolder.tv_comment = (TextView) v.findViewById(R.id.tv_comment);
                mHolder.siv = (SmartImageView) v.findViewById(R.id.iv);
                //把ViewHolder對象封裝至View對象中
                v.setTag(mHolder);
            }
            else{
                v = convertView;
                mHolder = (ViewHolder) v.getTag();
            }
            //給三個文本框設置內容
            mHolder.tv_title.setText(news.getTitle());

            mHolder.tv_detail.setText(news.getDetail());

            mHolder.tv_comment.setText(news.getComment() + "條評論");

            //給新聞圖片imageview設置內容
            mHolder.siv.setImageUrl(news.getImageUrl());
            return v;
        }

        class ViewHolder{
            //條目的布局文件中有什麼組件,這裡就定義什麼屬性
            TextView tv_title;
            TextView tv_detail;
            TextView tv_comment;
            SmartImageView siv;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }
    }
    private void getNewsInfo() {
        Thread t = new Thread(){
            @Override
            public void run() {
                String path = "http://192.168.13.13:8080/news.xml";
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);
                    //發送http GET請求,獲取相應碼
                    if(conn.getResponseCode() == 200){
                        InputStream is = conn.getInputStream();
                        //使用pull解析器,解析這個流
                        parseNewsXml(is);
                    }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        t.start();
    }

    private void parseNewsXml(InputStream is) {
        XmlPullParser xp = Xml.newPullParser();
        try {
            xp.setInput(is, "utf-8");
            //對節點的事件類型進行判斷,就可以知道當前節點是什麼節點
            int type = xp.getEventType();
            News news = null;
            while(type != XmlPullParser.END_DOCUMENT){
                switch (type) {
                case XmlPullParser.START_TAG:
                    if("newslist".equals(xp.getName())){
                        newsList = new ArrayList();
                    }
                    else if("news".equals(xp.getName())){
                        news = new News();
                    }
                    else if("title".equals(xp.getName())){
                        String title = xp.nextText();
                        news.setTitle(title);
                    }
                    else if("detail".equals(xp.getName())){
                        String detail = xp.nextText();
                        news.setDetail(detail);
                    }
                    else if("comment".equals(xp.getName())){
                        String comment = xp.nextText();
                        news.setComment(comment);
                    }
                    else if("image".equals(xp.getName())){
                        String image = xp.nextText();
                        news.setImageUrl(image);
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if("news".equals(xp.getName())){
                        newsList.add(news);
                    }
                    break;

                }
                //解析完當前節點後,把指針移動至下一個節點,並返回它的事件類型
                type = xp.next();
            }
            //發消息,讓主線程設置listview的適配器,如果消息不需要攜帶數據,可以發送空消息
            handler.sendEmptyMessage(1);
//          for (News n : newsList) {
//              System.out.println(n.toString());
//          }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved