Xml 格式数据的生成和解析
相關閱讀
什么是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注釋。不看都會
什么是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的中文亂碼問題解決
保存時候的編碼和設置打開時候的編碼一致,不會出現亂碼
轉義字符
因為在xml文檔中有些字符是特殊的,不能使用它們作為文本數據。例如:不能使用“<”或“>”等字符作為文本數據,所以需要使用轉義字符來表示。
例如<a><a></a>,你可能會說,其中第二個<a>是a元素的文本內容,而不是一個元素的開始標簽,但xml解析器是不會明白你的意思的。
把<a><a></a>修飾為<a><a></a>,這就OK了。
轉義字符都是以“&”開頭,以“;”結束。這與后面我們學習的實體是相同的。
CDATA區(CDATA段)
當大量的轉義字符出現在xml文檔中時,會使xml文檔的可讀性大幅度降低。這時如果使用CDATA段就會好一些。
在CDATA段中出現的“<”、“>”、“””、“’”、“&”,都無需使用轉義字符。這可以提高xml文檔的可讀性
<a><![CDATA[<a>]]></a>在CDATA段中不能包含“]]>”,即CDATA段的結束定界符
XML實戰案例
使用xml 作為數據交互的載體是Android 中非常重要的功能,比如天氣預報數據、短信備份數據、通訊錄數據都可以以xml 的格式通過網絡傳輸。
為了演示Xml 數據的操作,我模擬了一個短信備份的案例。
需求:界面如下圖所示。上面是三個Button,前兩個分別對應兩種不同方式生成xml,第三個Button點擊后解析備份的xml 文件,然后將數據展現在下面的ScrollView 中。短信數據是模擬的假數據。
生成的xml 格式如下
<?xml version="1.0" encoding="utf-8" standalone="yes" ?><smses><sms><address>5554</address><body>我是內容<>0</body><time>1445595309201</time></sms><sms><address>5555</address><body>我是內容<>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("我是短信內容" + i);sb.append("</body>");sb.append("<time>");sb.append(new Date().getTime());sb.append("</time>");sb.append("</sms>");}sb.append("</smses>");//使用系統提供的方法獲取文件輸出流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 的頭信息,其本質是寫入內容:* "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"*/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<Sms> 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<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());}//如果是“結束標簽”事件} 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<Sms> 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 解析器的工作方式為允許你的應用程序代碼主動從解析器中獲取事件,正因為是主動獲取事件,因此可以在滿足了需要的條件后不再獲取事件,結束解析
簡單新聞客戶端
新聞數據
<?xml version="1.0" encoding="UTF-8" ?> <newslist><news><title>黑馬52期就業快報</title><detail>熱烈祝賀黑馬52期平均薪水突破13k</detail><comment>15687</comment><image>http://192.168.1.100:8080/images/6.jpg</image></news><news><title>程序員因寫代碼太亂被殺害</title><detail>兇手是死者同事,維護死者代碼時完全看不懂而痛下殺手</detail><comment>16359</comment><image>http://192.168.1.100:8080/images/7.jpg</image></news><news><title>產品經理因頻繁改需求被殺害</title><detail>兇手是一名程序員,因死者對項目需求頻繁改動而痛下殺手</detail><comment>14112</comment><image>http://192.168.1.100:8080/images/7.jpg</image></news><news><title>3Q大戰宣判: 騰訊獲賠500萬</title><detail>最高法駁回360上訴, 維持一審宣判.</detail><comment>6427</comment><image>http://192.168.1.100:8080/images/1.jpg</image></news><news><title>今日之聲:北大雕塑被戴口罩</title><detail>市民: 因霧霾起訴環保局; 公務員談"緊日子": 堅決不出去.</detail><comment>681</comment><image>http://192.168.1.100:8080/images/2.jpg</image></news><news><title>奧巴馬見達賴是裝蒜</title><detail>外文局: 國際民眾認可中國大國地位;法院: "流量清零"未侵權.</detail><comment>1359</comment><image>http://192.168.1.100:8080/images/3.jpg</image></news><news><title>輕松一刻: 我要沉迷學習不自拔</title><detail>放假時我醒了不代表我起床了, 如今我起床了不代表我醒了!</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>問君能有幾多愁,恰似調完代碼改需求</detail><comment>13230</comment><image>http://192.168.1.100:8080/images/8.jpg</image></news><news><title>趙帥哥語錄三</title><detail>覺得我帥的人工資一般都比較高</detail><comment>9928</comment><image>http://192.168.1.100:8080/images/8.jpg</image></news><news><title>今日之聲:北大雕塑被戴口罩</title><detail>市民: 因霧霾起訴環保局; 公務員談"緊日子": 堅決不出去.</detail><comment>681</comment><image>http://192.168.1.100:8080/images/2.jpg</image></news><news><title>奧巴馬見達賴是裝蒜</title><detail>外文局: 國際民眾認可中國大國地位;法院: "流量清零"未侵權.</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="這是大標題志哥教你上塑料"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條評論"android:textColor="#ff0000"android:layout_alignParentRight="true"android:layout_below="@id/tv_detail"/> </RelativeLayout>實體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;} }實現代碼
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); // //要保證在設置適配器時,新聞xml文件已經解析完畢了 // lv.setAdapter(new MyAdapter());}class MyAdapter extends BaseAdapter{//得到模型層中元素的數量,用來確定listview需要有多少個條目@Overridepublic int getCount() {// TODO Auto-generated method stubreturn 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;}@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);//發送http GET請求,獲取相應碼if(conn.getResponseCode() == 200){InputStream is = conn.getInputStream();//使用pull解析器,解析這個流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");//對節點的事件類型進行判斷,就可以知道當前節點是什么節點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;}//解析完當前節點后,把指針移動至下一個節點,并返回它的事件類型type = xp.next();}//發消息,讓主線程設置listview的適配器,如果消息不需要攜帶數據,可以發送空消息handler.sendEmptyMessage(1); // for (News n : newsList) { // System.out.println(n.toString()); // }} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}} }總結
以上是生活随笔為你收集整理的Xml 格式数据的生成和解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Support Libr
- 下一篇: XML约束