用SAX和PULL進行XML文件的解析與生成

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>

 

源碼下載:http://download.csdn.net/detail/shark0017/8028375

相關文章
相關標籤/搜索