Xml 格式数据的生成和解析
相關(guān)閱讀
什么是XML
XML全稱為Extensible Markup Language, 意思是可擴(kuò)展的標(biāo)記語言,它是 SGML(標(biāo)準(zhǔn)通用標(biāo)記語言)的一個(gè)子集。
XML語法上和HTML比較相似,但HTML中的元素是固定的,而XML的標(biāo)簽是可以由用戶自定義的。
W3C在1998年2月發(fā)布1.0版本;
W3C在2004年2月發(fā)布1.1版本,但因?yàn)?.1版本不能向下兼容1.0版本,所以1.1沒有人用。同時(shí),在2004年2月W3C又發(fā)布了1.0版本的第三版。我們要學(xué)習(xí)的還是1.0版本!!!
W3C組織
W3C是萬維網(wǎng)聯(lián)盟(World Wide Web Consortium)英文的縮寫,它成立于1994年10月,以開放論壇的方式來促進(jìn)開發(fā)互通技術(shù)(包括規(guī)格、指南、軟件和工具),開發(fā)網(wǎng)絡(luò)的全部潛能。萬維網(wǎng)聯(lián)盟(W3C)從1994年成立以來,已發(fā)布了90多份Web技術(shù)規(guī)范,領(lǐng)導(dǎo)著Web技術(shù)向前發(fā)展。
W3C認(rèn)為自身不是官方組織,因此將它正式發(fā)布的規(guī)范稱為推薦(建議)標(biāo)準(zhǔn),意思是進(jìn)一步標(biāo)準(zhǔn)化的建議,但是由于組織自身的權(quán)威性往往成為事實(shí)上的標(biāo)準(zhǔn)。
XML的作用
- 程序的配置文件(這也是最后大家使用XML最常見的目的);
- 數(shù)據(jù)交換:不同語言之間用來交換數(shù)據(jù);
- 小型數(shù)據(jù)庫:用來當(dāng)數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)。
XML與HTML比較
- HTML的元素都是固定的,而XML可以自定義元素;
- HTML用瀏覽器來解析執(zhí)行, XML的解析器通常需要自己來寫(因?yàn)樵厥亲远x的);
- HTML只能用來表示網(wǎng)頁,而XML可以做的事情很多。
XML和properties(屬性文件)比較
- 屬性文件只能存儲(chǔ)平面信息,而XML可以存儲(chǔ)結(jié)構(gòu)化信息;
- 解析屬性文件只需要使用Properties類就可以了,而解析XML文檔是很復(fù)雜的。
XML文檔的組成部分
- XML文檔聲明;重要
- XML處理指令;看完了,就可以忘了!
- XML元素;最重要
- XML特殊字符和CDATA區(qū);一看就會(huì)
- XML注釋。不看都會(huì)
什么是xml文檔聲明
可以把xml文檔聲明看成是xml文檔說明。
最簡單的xml文檔聲明:< ?xml version=”1.0”? >
注意,XML是區(qū)別大小寫,這一點(diǎn)不同與HTML!
xml文檔聲明結(jié)構(gòu)
version屬性
用于說明當(dāng)前xml文檔的版本,因?yàn)槎际窃谟?.0,所以這個(gè)屬性值大家都寫1.0,version屬性是必須的;encoding屬性
用于說明當(dāng)前xml文檔使用的字符編碼集,xml解析器會(huì)使用這個(gè)編碼來解析xml文檔。encoding屬性是可選的,默認(rèn)為UTF-8。注意,如果當(dāng)前xml文檔使用的字符編碼集是gb2312,而encoding屬性的值為UTF-8,那么一定會(huì)出錯(cuò)的;standalone屬性
用于說明當(dāng)前xml文檔是否為獨(dú)立文檔,如果該屬性值為yes,表示當(dāng)前xml文檔是獨(dú)立的,如果為no表示當(dāng)前xml文檔不是獨(dú)立的,即依賴外部的文件。默認(rèn)是yes沒有xml文檔聲明的xml文檔,不是格式良好的xml文檔;
- xml文檔聲明必須從xml文檔的1行1列開始。
xml的中文亂碼問題解決
保存時(shí)候的編碼和設(shè)置打開時(shí)候的編碼一致,不會(huì)出現(xiàn)亂碼
轉(zhuǎn)義字符
因?yàn)樵趚ml文檔中有些字符是特殊的,不能使用它們作為文本數(shù)據(jù)。例如:不能使用“<”或“>”等字符作為文本數(shù)據(jù),所以需要使用轉(zhuǎn)義字符來表示。
例如<a><a></a>,你可能會(huì)說,其中第二個(gè)<a>是a元素的文本內(nèi)容,而不是一個(gè)元素的開始標(biāo)簽,但xml解析器是不會(huì)明白你的意思的。
把<a><a></a>修飾為<a><a></a>,這就OK了。
轉(zhuǎn)義字符都是以“&”開頭,以“;”結(jié)束。這與后面我們學(xué)習(xí)的實(shí)體是相同的。
CDATA區(qū)(CDATA段)
當(dāng)大量的轉(zhuǎn)義字符出現(xiàn)在xml文檔中時(shí),會(huì)使xml文檔的可讀性大幅度降低。這時(shí)如果使用CDATA段就會(huì)好一些。
在CDATA段中出現(xiàn)的“<”、“>”、“””、“’”、“&”,都無需使用轉(zhuǎn)義字符。這可以提高xml文檔的可讀性
<a><![CDATA[<a>]]></a>在CDATA段中不能包含“]]>”,即CDATA段的結(jié)束定界符
XML實(shí)戰(zhàn)案例
使用xml 作為數(shù)據(jù)交互的載體是Android 中非常重要的功能,比如天氣預(yù)報(bào)數(shù)據(jù)、短信備份數(shù)據(jù)、通訊錄數(shù)據(jù)都可以以xml 的格式通過網(wǎng)絡(luò)傳輸。
為了演示Xml 數(shù)據(jù)的操作,我模擬了一個(gè)短信備份的案例。
需求:界面如下圖所示。上面是三個(gè)Button,前兩個(gè)分別對(duì)應(yīng)兩種不同方式生成xml,第三個(gè)Button點(diǎn)擊后解析備份的xml 文件,然后將數(shù)據(jù)展現(xiàn)在下面的ScrollView 中。短信數(shù)據(jù)是模擬的假數(shù)據(jù)。
生成的xml 格式如下
<?xml version="1.0" encoding="utf-8" standalone="yes" ?><smses><sms><address>5554</address><body>我是內(nèi)容<>0</body><time>1445595309201</time></sms><sms><address>5555</address><body>我是內(nèi)容<>1</body><time>1445595309201</time></sms></smses>編寫布局文件
<?xml version="1.0" encoding="utf-8"?> <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:orientation="vertical"><Button android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="click1"android:text="生成xml1"/><Button android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="click2"android:text="生成xml2"/><Button android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="click3"android:text="解析xml"/><ScrollView android:layout_width="match_parent"android:layout_height="wrap_content"><TextView android:id="@+id/tv_sms"android:layout_width="match_parent"android:layout_height="wrap_content"/></ScrollView></LinearLayout>拼接字符串方式生成Xml 文件
//第一種方式生成xmlpublic void click1 (View view)throws Exception {StringBuilder sb = new StringBuilder();sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");sb.append("<smses>");for (int i = 0; i < 50; i++) {sb.append("<sms>");sb.append("<address>");sb.append(5554 + i);sb.append("</address>");sb.append("<body>");sb.append("我是短信內(nèi)容" + i);sb.append("</body>");sb.append("<time>");sb.append(new Date().getTime());sb.append("</time>");sb.append("</sms>");}sb.append("</smses>");//使用系統(tǒng)提供的方法獲取文件輸出流FileOutputStream fos = this.openFileOutput("info.xml", MODE_PRIVATE);fos.write(sb.toString().getBytes());fos.close();}上面的代碼雖然也可以生成xml 文件,但是無法對(duì)特殊字符進(jìn)行處理,比如如果短信內(nèi)容包含“
使用XmlSerializer 生成Xml 文件
/*** 第二種方式生成xml* 使用Android 提供的API*/public void click2 (View view)throws Exception {//在data 目錄中創(chuàng)建info2.xml 文件,獲取輸出流FileOutputStream fos = openFileOutput("info2.xml", MODE_PRIVATE);//通過Xml 類創(chuàng)建一個(gè)Xml 序列化器XmlSerializer serializer = Xml.newSerializer();//給序列化器設(shè)置輸出流和輸出流編碼serializer.setOutput(fos, "utf-8");/*** 讓序列化器開發(fā)寫入xml 的頭信息,其本質(zhì)是寫入內(nèi)容:* "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"*/serializer.startDocument("utf-8", true);/*** 開始寫入smses 標(biāo)簽,* 第一個(gè)參數(shù)是該標(biāo)簽的命名空間, 這里不需要*/serializer.startTag(null, "smses");for (int i = 0; i < 50; i++) {//開始sms 標(biāo)簽serializer.startTag(null, "sms");//開始address 標(biāo)簽serializer.startTag(null, "address");//在address 標(biāo)簽中間寫入文本serializer.text("" + (5554 + i));//結(jié)束address 標(biāo)簽serializer.endTag(null, "address");//開始body 標(biāo)簽serializer.startTag(null, "body");//在body 標(biāo)簽中間寫入文本serializer.text("我是內(nèi)容<>" + i);//結(jié)束body 標(biāo)簽serializer.endTag(null, "body");serializer.startTag(null, "time");serializer.text(new Date().getTime() + "");serializer.endTag(null, "time");//結(jié)束sms 標(biāo)簽serializer.endTag(null, "sms");}//結(jié)束smses 標(biāo)簽serializer.endTag(null, "smses");//結(jié)束文檔serializer.endDocument();fos.close();}使用XmlSerializer 生成xml 文件是推薦的方式,因?yàn)樵揳pi 內(nèi)部已經(jīng)實(shí)現(xiàn)了對(duì)特殊字符的處理
使用Pull 解析Xml 格式數(shù)據(jù)
asserts 資源目錄中的文件只能讀不能寫,多用于隨apk 一起發(fā)布的固定不變的數(shù)據(jù),比如可以將國內(nèi)所有的城市列表放在里面。
這里將生成的info2.xml 放到asserts 目錄中,然后讀取、解析、展現(xiàn)
//使用pull 解析xml 數(shù)據(jù)public void click3(View v) throws Exception {/*** 將解析出來的數(shù)據(jù)封裝在Sms 中,然后保存到ArrayList 中* Sms 是自定義的一個(gè)JavaBean*/ArrayList<Sms> smses = null;Sms sms = null;//調(diào)用父類提供的getAssets()方法獲取AssertManager 對(duì)象AssetManager assetManager = getAssets();//使用assetManager 的open 方法直接獲取輸入流對(duì)象InputStream inputStream = assetManager.open("info2.xml");//通過Xml 的靜態(tài)方法獲取Xml 解析器XmlPullParser parser = Xml.newPullParser();//設(shè)置輸入流和編碼parser.setInput(inputStream, "utf-8");//獲取事件類型int event = parser.next();//如果沒有解析到文檔的結(jié)尾,則循環(huán)解析while (event != XmlPullParser.END_DOCUMENT) {//獲取當(dāng)前解析到的標(biāo)簽名稱String tagName = parser.getName();//如果是“開始標(biāo)簽”事件if (event == XmlPullParser.START_TAG) {//判斷當(dāng)前解析到的開始標(biāo)簽是哪個(gè)if ("smses".equals(tagName)) {smses = new ArrayList<Sms>();} 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());}//如果是“結(jié)束標(biāo)簽”事件} else if (event == XmlPullParser.END_TAG) {if ("sms".equals(tagName)) {//如果是sms 結(jié)尾,則將創(chuàng)建的sms 對(duì)象添加到集合中smses.add(sms);}}//繼續(xù)獲取下一個(gè)事件event = parser.next();}inputStream.close();//將數(shù)據(jù)展示到界面showSms(smses);}/*** 將短信顯示到TextView 中*/private void showSms(ArrayList<Sms> smses) {StringBuilder sb = new StringBuilder();for (Sms s : smses) {sb.append(s.toString() + "\n");}tv_sms.setText(sb.toString());}Pull 解析和SAX 解析對(duì)比
Pull 解析器的運(yùn)行方式與SAX 解析器相似,都屬于事件驅(qū)動(dòng)模式。它提供了類似的事件,如:開始元素和結(jié)束元素事件,使用parser.next()可以進(jìn)入下一個(gè)元素并觸發(fā)相應(yīng)事件。事件將作為數(shù)值代碼被發(fā)送,因此可以使用一個(gè)switch 對(duì)感興趣的事件進(jìn)行處理。當(dāng)元素開始解析時(shí),調(diào)用parser.nextText()方法可以獲取下一個(gè)Text 類型元素的值
SAX 解析器的工作方式是自動(dòng)將事件推入事件處理器進(jìn)行處理,因此你不能控制事件的處理主動(dòng)結(jié)束;而Pull 解析器的工作方式為允許你的應(yīng)用程序代碼主動(dòng)從解析器中獲取事件,正因?yàn)槭侵鲃?dòng)獲取事件,因此可以在滿足了需要的條件后不再獲取事件,結(jié)束解析
簡單新聞客戶端
新聞數(shù)據(jù)
<?xml version="1.0" encoding="UTF-8" ?> <newslist><news><title>黑馬52期就業(yè)快報(bào)</title><detail>熱烈祝賀黑馬52期平均薪水突破13k</detail><comment>15687</comment><image>http://192.168.1.100:8080/images/6.jpg</image></news><news><title>程序員因?qū)懘a太亂被殺害</title><detail>兇手是死者同事,維護(hù)死者代碼時(shí)完全看不懂而痛下殺手</detail><comment>16359</comment><image>http://192.168.1.100:8080/images/7.jpg</image></news><news><title>產(chǎn)品經(jīng)理因頻繁改需求被殺害</title><detail>兇手是一名程序員,因死者對(duì)項(xiàng)目需求頻繁改動(dòng)而痛下殺手</detail><comment>14112</comment><image>http://192.168.1.100:8080/images/7.jpg</image></news><news><title>3Q大戰(zhàn)宣判: 騰訊獲賠500萬</title><detail>最高法駁回360上訴, 維持一審宣判.</detail><comment>6427</comment><image>http://192.168.1.100:8080/images/1.jpg</image></news><news><title>今日之聲:北大雕塑被戴口罩</title><detail>市民: 因霧霾起訴環(huán)保局; 公務(wù)員談"緊日子": 堅(jiān)決不出去.</detail><comment>681</comment><image>http://192.168.1.100:8080/images/2.jpg</image></news><news><title>奧巴馬見達(dá)賴是裝蒜</title><detail>外文局: 國際民眾認(rèn)可中國大國地位;法院: "流量清零"未侵權(quán).</detail><comment>1359</comment><image>http://192.168.1.100:8080/images/3.jpg</image></news><news><title>輕松一刻: 我要沉迷學(xué)習(xí)不自拔</title><detail>放假時(shí)我醒了不代表我起床了, 如今我起床了不代表我醒了!</detail><comment>11616</comment><image>http://192.168.1.100:8080/images/4.jpg</image></news><news><title>男女那些事兒</title><detail>"媽, 我在東莞被抓, 要2萬保釋金, 快匯錢到xxx!"</detail><comment>10339</comment><image>http://192.168.1.100:8080/images/5.jpg</image></news><news><title>趙帥哥語錄一</title><detail>少壯不努力,老大做IT</detail><comment>14612</comment><image>http://192.168.1.100:8080/images/8.jpg</image></news><news><title>趙帥哥語錄二</title><detail>問君能有幾多愁,恰似調(diào)完代碼改需求</detail><comment>13230</comment><image>http://192.168.1.100:8080/images/8.jpg</image></news><news><title>趙帥哥語錄三</title><detail>覺得我?guī)浀娜斯べY一般都比較高</detail><comment>9928</comment><image>http://192.168.1.100:8080/images/8.jpg</image></news><news><title>今日之聲:北大雕塑被戴口罩</title><detail>市民: 因霧霾起訴環(huán)保局; 公務(wù)員談"緊日子": 堅(jiān)決不出去.</detail><comment>681</comment><image>http://192.168.1.100:8080/images/2.jpg</image></news><news><title>奧巴馬見達(dá)賴是裝蒜</title><detail>外文局: 國際民眾認(rèn)可中國大國地位;法院: "流量清零"未侵權(quán).</detail><comment>1359</comment><image>http://192.168.1.100:8080/images/3.jpg</image></news> </newslist>布局文件
<RelativeLayout 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"tools:context=".MainActivity" ><ListView android:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>ListView的item布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content" ><com.loopj.android.image.SmartImageView android:id="@+id/iv"android:layout_width="90dp"android:layout_height="70dp"android:src="@drawable/ic_launcher"android:layout_centerVertical="true"/><TextView android:id="@+id/tv_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="這是大標(biāo)題志哥教你上塑料"android:layout_toRightOf="@id/iv"android:textSize="22sp"android:singleLine="true"/><TextView android:id="@+id/tv_detail"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="這是正文志哥教你帶崩三路"android:layout_toRightOf="@id/iv"android:layout_below="@id/tv_title"android:textSize="15sp"android:textColor="@android:color/darker_gray"android:lines="2"/><TextView android:id="@+id/tv_comment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="65031條評(píng)論"android:textColor="#ff0000"android:layout_alignParentRight="true"android:layout_below="@id/tv_detail"/> </RelativeLayout>實(shí)體bean
public class News {private String title;private String detail;private String comment;private String imageUrl;@Overridepublic 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;} }實(shí)現(xiàn)代碼
public class MainActivity extends Activity {List<News> newsList;Handler handler = new Handler(){public void handleMessage(android.os.Message msg) {ListView lv = (ListView) findViewById(R.id.lv);lv.setAdapter(new MyAdapter());}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getNewsInfo(); // ListView lv = (ListView) findViewById(R.id.lv); // //要保證在設(shè)置適配器時(shí),新聞xml文件已經(jīng)解析完畢了 // lv.setAdapter(new MyAdapter());}class MyAdapter extends BaseAdapter{//得到模型層中元素的數(shù)量,用來確定listview需要有多少個(gè)條目@Overridepublic int getCount() {// TODO Auto-generated method stubreturn newsList.size();}@Override//返回一個(gè)View對(duì)象,作為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();//把布局文件中所有組件的對(duì)象封裝至ViewHolder對(duì)象中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對(duì)象封裝至View對(duì)象中v.setTag(mHolder);}else{v = convertView;mHolder = (ViewHolder) v.getTag();}//給三個(gè)文本框設(shè)置內(nèi)容mHolder.tv_title.setText(news.getTitle());mHolder.tv_detail.setText(news.getDetail());mHolder.tv_comment.setText(news.getComment() + "條評(píng)論");//給新聞圖片imageview設(shè)置內(nèi)容mHolder.siv.setImageUrl(news.getImageUrl());return v;}class ViewHolder{//條目的布局文件中有什么組件,這里就定義什么屬性TextView tv_title;TextView tv_detail;TextView tv_comment;SmartImageView siv;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}}private void getNewsInfo() {Thread t = new Thread(){@Overridepublic 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);//發(fā)送http GET請(qǐng)求,獲取相應(yīng)碼if(conn.getResponseCode() == 200){InputStream is = conn.getInputStream();//使用pull解析器,解析這個(gè)流parseNewsXml(is);}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}};t.start();}private void parseNewsXml(InputStream is) {XmlPullParser xp = Xml.newPullParser();try {xp.setInput(is, "utf-8");//對(duì)節(jié)點(diǎn)的事件類型進(jìn)行判斷,就可以知道當(dāng)前節(jié)點(diǎn)是什么節(jié)點(diǎn)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<News>();}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;}//解析完當(dāng)前節(jié)點(diǎn)后,把指針移動(dòng)至下一個(gè)節(jié)點(diǎn),并返回它的事件類型type = xp.next();}//發(fā)消息,讓主線程設(shè)置listview的適配器,如果消息不需要攜帶數(shù)據(jù),可以發(fā)送空消息handler.sendEmptyMessage(1); // for (News n : newsList) { // System.out.println(n.toString()); // }} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}} }總結(jié)
以上是生活随笔為你收集整理的Xml 格式数据的生成和解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Support Libr
- 下一篇: XML约束