XML解析有傳統的dom方法還有Jsoup,SAX,PULL等,這裏講的是比較省內存的SAX和PULL方法。Android中極力推薦用PULL的方式來解析,我我的以爲pull確實比較簡單,但其內部的邏輯性不是很分明。因此今天作了個類來將其中的多個步驟進行了分割,之後直接拿來用便可。java
1.SAX:android
首先先講解SAX中各個方法的做用:app
咱們以這個不規則的xml語句作例子:框架
<abc:kale sex=m age=21>jack</abc:kale>dom
startDocument:開始解析一個xml文件時觸發ide
endDocument:這個xml文件被解析完畢時觸發佈局
startElement:開始解析xml文件中的一個標籤時觸發,這裏能夠獲得標籤名和其中的各個屬性值。post
如:從<person age = 12 sex = f/>會獲得標籤名:【person】和屬性值:【age = 12 sex = f】測試
endElement:結束解析一個標籤時觸發this
characters:解析這個標籤內部的內容時觸發,這裏能夠獲得這個標籤子節點中的內容。
如:從<name>jack<name>中獲得【jack】
下面是實現代碼:
1.首先創建一個SAX對象,而後進行解析工做。這裏會要本身創建一個ContentHandler的子類
/** * 經過sax進行解析 * @param str */ public void sax(String str) { //下面是固定的寫法 SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader reader; try { //獲得xmlReader對象 reader = factory.newSAXParser().getXMLReader(); //設置內容處理器 reader.setContentHandler(new MyContentHandler()); reader.parse(new InputSource(new StringReader(str))); } catch (SAXException | ParserConfigurationException | IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } }
MyContentHandler.java 這個類就是來處理事務的,裏面有各類回調方法
package com.kale.xml; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * @author:Jack Tony * @tips :舉個極端的例子:<input type="hidden" name="UserType">kale</input> * startElement中能夠獲得的是:type="hidden" name="UserType" * characters中獲得的是:kale * @date :2014-10-11 */ public class MyContentHandler extends DefaultHandler{ //當前正在解析的標籤名 private String currentTag; /* * 開始解析這個xml文件的時候觸發 */ @Override public void startDocument() throws SAXException { System.out.println("開始解析這個文件了"); } /* * 結束解析這個xml文件的時候觸發 */ @Override public void endDocument() throws SAXException { System.out.println("文件解析結束"); } /* * 開始解析每一個元素的時候觸發 * <person age = 12 sex = f/> * <kale:name>jack<kale:name> * 1.uri:當前正在解析的元素的命名空間 * 2.localName:不帶前綴的這個元素的名字——>name * 3.qName:帶前綴的這個元素命——>kale:name * 4.attributes:獲得的元素中的屬性——>age=12 set=f */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //舉例:<input type="hidden" name="UserType" id="UserType" value="1"> currentTag = localName;//input System.out.println("————開始解析"+qName+"這個標籤了————"); for (int i = 0; i < attributes.getLength(); i++) { String name = attributes.getLocalName(i);//第一次是:type String value = attributes.getValue(i);//第一次是:hidden System.out.println(name + " = " + value); } } /* (非 Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) * 中止解析這個元素的時候觸發 */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { // TODO 自動生成的方法存根 super.endElement(uri, localName, qName); System.out.println("————-解析"+qName+"標籤結束————"); } /* * 獲得元素中的內容,好比下面的jack * <name>jack<name> */ @Override public void characters(char[] ch, int start, int length) throws SAXException { //舉例:<name>jack<name> if (currentTag.equals("name")) { System.out.println("name = " + new String(ch,start,length));//會輸出jack } if (currentTag.equals("age")) { System.out.println("age = " + new String(ch,start,length));//會輸出21 } } }
貼上測試樣本(因爲xml文件多是不規範的,因此處理時要考慮異常):
<?xml version="1.0" encoding="utf-8"?> <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" > <input type="hidden" name="UserType" id="12" value="1"/> <abc:name>jack</abc:name> <abc:age>21</abc:age> </form> </namespace>
測試結果:
2.PULL
其實PULL中就一個重要的方法XmlPullParser.next();,正因如此才讓其變得簡單不少。PULL的特色是運行到什麼狀態是沒有回調方法的,它進行某個處理狀態時,會改變一個狀態變量,經過getEventType()就能夠來判斷當前是處於什麼狀態了。
推薦瀏覽:http://384444165.iteye.com/blog/1521332
但正由於內部邏輯須要開發者來處理,因此變得結構不是很清晰。我這裏經過一個類來將其轉換爲SAX的框架,之後只須要複寫這些方法即可以直接進行操做了。至於運行到哪一步,看方法名酒明白了。一樣仍是以前的那幅圖:
這裏面的方法的做用也是和SAX同樣的。下面是使用的代碼:
1.創建這個類的對象,執行操做
/** * 經過pull進行解析 * @param str */ public void pull(String str) { XmlPullParser parser = Xml.newPullParser(); InputStream in = new ByteArrayInputStream(str.getBytes()); try { parser.setInput(in,"utf-8"); MyXmlPullParserTask task = new MyXmlPullParserTask(parser); task.execute();//開始解析文件 } catch (XmlPullParserException e) { e.printStackTrace(); } }
2.進行解析處理
MyXmlPullParserTask.java
我經過前面的switch-case語句將處理的狀態進行了分割,這些狀態能夠徹底類比到SAX中。這樣便於理解!
package com.kale.xml; import java.io.IOException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class MyXmlPullParserTask { /** * 當前正在解析的標籤名,相似於sax中的localName * 能夠獲得<abc:kale sex=m age=21>jack</abc:kale>中【kale】 */ private String currentTag; /** * 當前正在解析的標籤名的前綴,sax中的qName=前綴+當前標籤名 * 能夠獲得<abc:kale sex=m age=21>jack</abc:kale>中【abc】 */ private String currentPrefix; /** * 當前正在解析的標籤的命名空間,相似於sax中的uri * 獲得 * <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> * <abc:kale sex=m age=21>jack</abc:kale> * 中的【http://schemas.android.com/apk/res/android】 */ private String currentNamespace; private XmlPullParser parser; public MyXmlPullParserTask(XmlPullParser parser) { this.parser = parser; } /** * 開始解析的方法,這裏已經寫好了,儘可能不要該這裏的代碼。 */ public void execute() { try { // 獲得當前狀態的標識代碼 int eventCode = parser.getEventType(); // 若是當前狀態不是文檔結束,那麼就繼續循環 boolean flag = true; while (flag) { // 當前解析元素的標籤,不帶前綴 currentTag = parser.getName(); currentNamespace = parser.getNamespace(); currentPrefix = parser.getPrefix(); switch (eventCode) { case XmlPullParser.START_DOCUMENT: startDocument(); break; case XmlPullParser.END_DOCUMENT: endDocument(); flag = false;// 到文檔末尾了,結束循環 break; case XmlPullParser.START_TAG: startElement(parser); characters(parser); break; case XmlPullParser.END_TAG: endElement(parser); break; default: break; } eventCode = parser.next(); } } catch (XmlPullParserException | IOException e) { e.printStackTrace(); } } /** * 開始解析文件時觸發的方法 */ public void startDocument() { System.out.println("開始解析這個文件了"); } /** * 結束解析這個xml文件的時候觸發 */ public void endDocument() { System.out.println("該文件解析完成"); } /** * 開始解析某個標籤時觸發 * 能夠獲得<abc:kale sex=m age=21>jack</abc:kale>中【sex=m age=21】部分 * @param parser */ public void startElement(XmlPullParser parser) { System.out.println("————開始解析" + currentPrefix +":"+ currentTag + "這個標籤了————"); for (int i = 0; i < parser.getAttributeCount(); i++) { String name = parser.getAttributeName(i); String value = parser.getAttributeValue(i); System.out.println(name + " = " + value); } } /** * 結束解析某個標籤時觸發 * 遇到/>時表示一個標籤解析完成,而遇到</xxx>不會觸發 * @param parser */ public void endElement(XmlPullParser parser) { System.out.println("————解析" + currentPrefix +":"+ currentTag + "標籤結束————"); } /** * 解析標籤中內容時觸發 * 獲得<name>jack</name>中【jack】的部分 * @param parser * @throws XmlPullParserException * @throws IOException */ public void characters(XmlPullParser parser) throws XmlPullParserException, IOException { if (currentTag.equals("name")) { System.out.println("name = " + parser.nextText());// 會輸出jack } else if (currentTag.equals("age")) { System.out.println("age = " + parser.nextText());// 會輸出21 } } }
測試樣本:
<?xml version="1.0" encoding="utf-8"?> <namespace xmlns:abc="http://schemas.android.com/apk/res/android"> <form id="form1" name="form1" method="post" action="ok" style="margin: 0; padding: 0;" > <input type="hidden" name="UserType" id="12" value="1"/> <abc:name>jack</abc:name> <abc:age>21</abc:age> </form> </namespace>
測試結果:
3.XML文件的生成
生成是用簡單的pull來作的,沒啥技術含量,就是用代碼來寫xml,最後放到sd卡中
/** * 創建一個xml文件 */ public void creatXML() { XmlSerializer serializer = Xml.newSerializer(); File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml"); FileOutputStream fos = null; try { fos = new FileOutputStream(file); serializer.setOutput(fos, "UTF-8"); serializer.startDocument("UTF-8", true); //命名空間+標籤名,命名空間能夠=null serializer.startTag(null, "namespace"); serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf"); serializer.startTag(null, "persons"); for (int i = 0; i < 2; i++) { serializer.startTag(null, "person"); serializer.attribute(null, "id", i+1+""); serializer.attribute(null, "age", i+10+""); serializer.startTag(null, "name"); serializer.text("jack"); serializer.endTag(null, "name"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endTag(null, "namespace"); serializer.endDocument(); } catch (IllegalArgumentException | IllegalStateException | IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } finally { try { fos.flush(); fos.close(); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } }
測試結果:
所有activity中的代碼
package com.kale.xml; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.util.Xml; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); creatXML(); Toast.makeText(this, "xml文件創建成功,在SD卡根目錄下sharpandroid.xml", 0).show(); } public void buttonListener(View v) { //從assets文件夾中獲得test.xml文件的內容 String str = getFromAssets("test.xml"); switch (v.getId()) { case R.id.sax_button: //經過sax進行文件的解析 sax(str); break; case R.id.pull_button: //經過pull來解析文件 pull(str); break; default: break; } } /** * 經過sax進行解析 * @param str */ public void sax(String str) { //下面是固定的寫法 SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader reader; try { //獲得xmlReader對象 reader = factory.newSAXParser().getXMLReader(); //設置內容處理器 reader.setContentHandler(new MyContentHandler()); reader.parse(new InputSource(new StringReader(str))); } catch (SAXException | ParserConfigurationException | IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } /** * 經過pull進行解析 * @param str */ public void pull(String str) { XmlPullParser parser = Xml.newPullParser(); InputStream in = new ByteArrayInputStream(str.getBytes()); try { parser.setInput(in,"utf-8"); MyXmlPullParserTask task = new MyXmlPullParserTask(parser); task.execute();//開始解析文件 } catch (XmlPullParserException e) { e.printStackTrace(); } } /** * @param fileName * @return assets中文件的字符串 */ public String getFromAssets(String fileName){ String result=""; try { InputStreamReader inputReader = new InputStreamReader( getResources().getAssets().open(fileName) ); BufferedReader bufReader = new BufferedReader(inputReader); String line=""; while((line = bufReader.readLine()) != null) { result += line; } } catch (Exception e) { e.printStackTrace(); } return result; } /** * 創建一個xml文件 */ public void creatXML() { XmlSerializer serializer = Xml.newSerializer(); File file = new File(Environment.getExternalStorageDirectory(),"sharpandroid.xml"); FileOutputStream fos = null; try { fos = new FileOutputStream(file); serializer.setOutput(fos, "UTF-8"); serializer.startDocument("UTF-8", true); //命名空間+標籤名,命名空間能夠=null serializer.startTag(null, "namespace"); serializer.attribute("http://schemas.android.com/apk/res/android", "abc", "egf"); serializer.startTag(null, "persons"); for (int i = 0; i < 2; i++) { serializer.startTag(null, "person"); serializer.attribute(null, "id", i+1+""); serializer.attribute(null, "age", i+10+""); serializer.startTag(null, "name"); serializer.text("jack"); serializer.endTag(null, "name"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endTag(null, "namespace"); serializer.endDocument(); } catch (IllegalArgumentException | IllegalStateException | IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } finally { try { fos.flush(); fos.close(); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } } }
佈局文件:
<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="${relativePackage}.${activityClass}" > <Button android:id="@+id/sax_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="經過SAX來解析" android:onClick="buttonListener"/> <Button android:id="@+id/pull_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:text="經過PULL來解析" android:onClick="buttonListener"/> </RelativeLayout>