前言
既然要說XML解析,那么先來聊聊什么是XML。XML,可擴展標記語言 (Extensible Markup Language) ,用於標記電子文件使其具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言,這是百度百科的解釋。而XML是一種在Internet中傳輸數據的常見格式,它與HTML一樣,都是SGML(標准通用標記語言),無論你是需要通過Internet訪問數據,或者發送數據給Web服務,都可能需要用到XML的知識。恰恰Android應用程序需要和網絡交互,否則只是一款單機的無互動的應用程序,所以很可能在Android應用程序開發的過程中使用到XML。
由於XML的擴展性強,致使它需要有穩定的基礎規則來支持擴展,該語法規則需要注意的是:
- 開始和結束標簽匹配。
- 嵌套標簽不能相互嵌套。
- 區分大小寫。
Android中的XML
Android平台最大的優勢在於,上層應用基本可以利用Java編程語言開發,Java平台支持通過許多不同的方式來使用XML,並且大多數與XML相關的API已經在Android系統上得到了完全的支持。但是因為Android這個移動設備的局限性,一般僅考慮使用三種方式解析XML:
- DOM,Document Object Model,文檔對象模型方式,解析完的XML將生成一個樹狀結構的對象。
- SAX,simple API for Xml,以事件的形式通知程序,對XML進行解析。
- XML PULL,類似於SAX方式,程序以拉取的方式對XML進行解析。
SAX
SAX是一種以事件驅動的XML API,由它定義的事件流可以指定從解析器傳到專門的處理程序的代碼的XML結構,簡單來講,它是解析速度快,占用內存少的接解析器,這種解析器比較適合Android等移動設備。
使用SAX的優點:
- 新建一個類MyHandler,繼承自DefaultHandler,並重寫DefaultHandler中的特有方法,解析XML的工作在此類中完成。
- 實例化一個SAX解析器的工廠對象,SAXParserFactory對象,使用SAXParserFactory.newInstance()方法獲取。
- 利用SAXParserFactory.newSAXParser()獲得SAX解析器對象SAXParser。
- 實例化MyHandler類,傳入需要解析的節點名稱。
- 使用SAXParser.parse()方法設置待解析的XML流和XML解析對象。
- 最后從MyHandler對象中獲得解析結果。
- public void startDocument():接受到一個XML文檔時候的通知。
- public void startElement(String uri, String localName, String qName,Attributes attributes):接受到一個開始元素的通知,並且可以在此獲得元素的屬性。
- public void characters(char[] ch, int start, int length):接收元素中字符數據的通知。
- public void endElement(String uri, String localName, String qName):接收結束元素的通知。
- public void endDocument():接受一個文檔的結束通知。
上面一些重寫的方法通過一個XML文件來講解一下什么時候被執行。
<?xml version="1.0" encoding="utf-8"?> startDocument
<persons> startElement
<person id="23"> startElement
<name nameid="1"> startElement
Jack characters
</name> endElement
<age> startElement
21 characters
</age> endElement
</person> endElement
</persons> endElement
SAXParserFactory類,定義了一個工廠API,使應用程序能夠配置和獲得基於SAX的解析器以解析XML文檔。它只有一個prctected的構造方法(單例模式),所以需要使用靜態的newInstance()方法來回的SAXParserFactory()對象。使用SAXParserFactory可以通過調用.newSAXParser()方法獲得一個SAXParser,通過SAXParser對象可以執行parser()方法,通過傳遞的參數設定XML流和解析器類。
示例程序
現在通過一個示例程序來講解一下SAX是怎么解析XML文件的,這個示例程序是運行在Android平台上的,為了模擬真實情況,在IIS服務器上放置了一個靜態的XML文件,通過Android程序去讀取XML文件中的內容,在這個示例程序中,讀取person節點的值。因為是Android程序,所以別忘了賦予其訪問網絡的權限。
XML文件內容:
1 <?xml version="1.0" encoding="utf-8"?> 2 <persons> 3 <person id="23"> 4 <name nameid="1">Jack</name> 5 <age>21</age> 6 </person> 7 <person id="20"> 8 <name nameid="2">Dick</name> 9 <age>23</age> 10 </person> 11 </persons>
步驟一,DefaultHandler類的繼承子類MyHandler:
1 package cn.bgxt.handler; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 7 import org.xml.sax.Attributes; 8 import org.xml.sax.SAXException; 9 import org.xml.sax.helpers.DefaultHandler; 10 11 public class MyHandler extends DefaultHandler { 12 13 private List<HashMap<String, String>> list = null; //解析后的XML內容 14 private HashMap<String, String> map = null; //存放當前需要記錄的節點的XML內容 15 private String currentTag = null;//當前讀取的XML節點 16 private String currentValue = null;//當前節點的XML文本值 17 private String nodeName = null;//需要解析的節點名稱 18 19 public MyHandler(String nodeName) { 20 // 設置需要解析的節點名稱 21 this.nodeName = nodeName; 22 } 23 24 @Override 25 public void startDocument() throws SAXException { 26 // 接收文檔開始的通知。 27 // 實例化ArrayList用於存放解析XML后的數據 28 list = new ArrayList<HashMap<String, String>>(); 29 } 30 31 @Override 32 public void startElement(String uri, String localName, String qName, 33 Attributes attributes) throws SAXException { 34 // 接收元素開始的通知。 35 if (qName.equals(nodeName)) { 36 //如果當前運行的節點名稱與設定需要讀取的節點名稱相同,則實例化HashMap 37 map = new HashMap<String, String>(); 38 } 39 //Attributes為當前節點的屬性值,如果存在屬性值,則屬性值也讀取。 40 if (attributes != null && map != null) { 41 for (int i = 0; i < attributes.getLength(); i++) { 42 //讀取到的屬性值,插入到Map中。 43 map.put(attributes.getQName(i), attributes.getValue(i)); 44 } 45 } 46 //記錄當前節點的名稱。 47 currentTag = qName; 48 } 49 50 @Override 51 public void characters(char[] ch, int start, int length) 52 throws SAXException { 53 // 接收元素中字符數據的通知。 54 //當前節點有值的情況下才繼續執行 55 if (currentTag != null && map != null) { 56 //獲取當前節點的文本值,ch這個直接數組就是存放的文本值。 57 currentValue = new String(ch, start, length); 58 if (currentValue != null && !currentValue.equals("") 59 && !currentValue.equals("\n")) { 60 //讀取的文本需要判斷不能為null、不能等於”“、不能等於”\n“ 61 map.put(currentTag, currentValue); 62 } 63 } 64 //讀取完成后,需要清空當前節點的標簽值和所包含的文本值。 65 currentTag = null; 66 currentValue = null; 67 } 68 69 @Override 70 public void endElement(String uri, String localName, String qName) 71 throws SAXException { 72 // 接收元素結束的通知。 73 if (qName.equals(nodeName)) { 74 //如果讀取的結合節點是我們需要關注的節點,則把map加入到list中保存 75 list.add(map); 76 //使用之后清空map,開始新一輪的讀取person。 77 map = null; 78 } 79 } 80 81 public List<HashMap<String, String>> getList() { 82 return list; 83 } 84 85 }
因為XML文件是放在服務器上的,需要寫一個HttpUitils類讀取服務器上XML文件,使用的是URLHttpConnection的方式讀取,如果不了解URLHttpConnection的朋友,可以看之前的博客:http://www.cnblogs.com/plokmju/p/java-HTTP.html。下面直接附上代碼:
1 package cn.bgxt.http; 2 3 import java.io.InputStream; 4 import java.net.HttpURLConnection; 5 import java.net.URL; 6 7 public class HttpUtils { 8 9 public HttpUtils() { 10 } 11 public static InputStream getXML(String path) { 12 try { 13 URL url=new URL(path); 14 if(url!=null) 15 { 16 HttpURLConnection connection=(HttpURLConnection)url.openConnection(); 17 connection.setDoInput(true); 18 connection.setConnectTimeout(3000); 19 connection.setRequestMethod("GET"); 20 int requesetCode=connection.getResponseCode(); 21 if(requesetCode==200) 22 { 23 //如果執行成功,返回HTTP響應流 24 return connection.getInputStream(); 25 } 26 } 27 } catch (Exception e) { 28 // TODO: handle exception 29 } 30 return null; 31 } 32 }
還需要一個調用SAXParser對象的類,這里新建一個SaxService類,實例化SAXParserFactory用於設定XML流和解析器。代碼如下:
1 package cn.bgxt.service; 2 3 import java.io.InputStream; 4 import java.util.HashMap; 5 import java.util.List; 6 7 import javax.xml.parsers.SAXParser; 8 import javax.xml.parsers.SAXParserFactory; 9 10 import cn.bgxt.handler.MyHandler; 11 12 public class SaxService { 13 14 public SaxService() { 15 // TODO Auto-generated constructor stub 16 } 17 18 public static List<HashMap<String, String>> readXML(InputStream inputStream,String nodeName) 19 { 20 try { 21 //實例化SAX工廠類 22 SAXParserFactory factory=SAXParserFactory.newInstance(); 23 //實例化SAX解析器。 24 SAXParser sParser=factory.newSAXParser(); 25 //實例化DefaultHandler,設置需要解析的節點 26 MyHandler myHandler=new MyHandler(nodeName); 27 // 開始解析 28 sParser.parse(inputStream, myHandler); 29 // 解析完成之后,關閉流 30 inputStream.close(); 31 //返回解析結果。 32 return myHandler.getList(); 33 } catch (Exception e) { 34 // TODO: handle exception 35 } 36 return null; 37 } 38 39 }
最后就是Android的一個Activity類了,布局界面很簡單,只有一個按鈕控件,這里不展示布局代碼了。點擊按鈕后,觸發點擊事件,因為是Android4.0+,所以不能在主線程中訪問網絡,需要另起一個線程,這里使用Thread類。代碼如下:
1 package cn.bgxt.androidxmlforsax; 2 3 import java.io.InputStream; 4 import java.util.HashMap; 5 import java.util.List; 6 import cn.bgxt.http.HttpUtils; 7 import cn.bgxt.service.SaxService; 8 import android.os.Bundle; 9 import android.app.Activity; 10 import android.view.Menu; 11 import android.view.View; 12 import android.widget.Button; 13 14 public class MainActivity extends Activity { 15 private Button btn; 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 21 btn=(Button)findViewById(R.id.btn); 22 btn.setOnClickListener(new View.OnClickListener() { 23 24 @Override 25 public void onClick(View v) { 26 //Android4.0+需要另起線程訪問網絡 27 Thread thread=new Thread(new Runnable() { 28 29 @Override 30 public void run() { 31 // 設置XML文檔的位置 32 String path="http://192.168.1.107:1231/persons.xml"; 33 //讀取服務器上的XML,獲取XML流 34 InputStream inputStream=HttpUtils.getXML(path); 35 try { 36 //解析流,設定需要解析的節點 37 List<HashMap<String, String>> list=SaxService.readXML(inputStream, "person"); 38 for(HashMap<String,String> map:list) 39 { 40 //打印到LogCat中 41 System.out.println(map.toString()); 42 } 43 } catch (Exception e) { 44 // TODO: handle exception 45 } 46 } 47 }); 48 thread.start(); 49 } 50 }); 51 } 52 53 @Override 54 public boolean onCreateOptionsMenu(Menu menu) { 55 // Inflate the menu; this adds items to the action bar if it is present. 56 getMenuInflater().inflate(R.menu.main, menu); 57 return true; 58 } 59 60 }
當點擊后,XML解析后的內容會把打印到日志中,可以使用LogCat查看。