Android之SAX生成XML及TransformerHandler解析

Android之SAX生成XML

文章鏈接:http://blog.csdn.net/qq_16628781/article/details/70174654

知識點:

  1. SAXTransformerFactory類及其對象說明;
  2. TransformerHandler類及其對象說明;
  3. Transformer類及其對象說明;
  4. SAX生成XML實例講解;
  5. 新名詞記錄{SAXTransformerFactory:sax轉換工廠類;TransformerHandler:轉換事件處理類,觸發節點事件;Transformer:設置輸出XML文檔的屬性等;AttributesImpl:設置節點屬性的類;}

概述

上一篇講解了如何利用pull生成XML文件。Android之pull生成XML及XmlSerializer詳解

下面我們來講一下如何利用SAX方式生成XML文件。

SAXTransformerFactory類及其對象

SAXTransformerFactory類繼承自TransformerFactory,利用此類,可以創建Transformer類和Templates類的對象(在javax.xml.transform包下面)。

關於SAXTransformerFactory類對象的獲取,使用的是下面代碼:

            SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();

去到靜態方法newInstance()可以看到如下:

public static TransformerFactory newInstance()
            throws TransformerFactoryConfigurationError {
        String className = "org.apache.xalan.processor.TransformerFactoryImpl";
        try {
            return (TransformerFactory) Class.forName(className).newInstance();
        } catch (Exception e) {
            throw new NoClassDefFoundError(className);
        }
    }

因爲SAXTransformerFactory的構造方法是受保護的,所以不能夠直接new出一個對象。這裏是利用反射獲取到SAXTransformerFactory類實例,主要是獲取到TransformerFactoryImpl類對象。關於TransformerFactoryImpl類,它是由Apache提供的類,放在org.apache.xalan.processor目錄下面。關於找不到該類的異常就不管了,系統都找不到,我們還能怎麼相信Google。

TransformerHandler類及其對象

獲取到SAXTransformerFactory工廠類對象之後,就可以獲取轉換處理類TransformerHandler的對象了。代碼如下:

TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();

那麼TransformerHandler類是用來幹嘛的呢?TransformerHandler類是用來監聽ContentHandler轉換事件和轉換數據到Result類裏面去(對應的設置是setResult(result)方法)。

在利用SAX進行解析XML的時候,我們自定義了一個類,然後繼承DefaultHandler處理類,在自定義的類裏面,需要重寫startElement()、characters()和endElement()等方法。DefaultHandler在繼承關係上面,我們也可以看到它是繼承了EntityResolver, DTDHandler, ContentHandler, ErrorHandler。看到其中有ContentHandler類。

那麼ContentHandler類的作用是什麼呢?簡單來說,SAX解析基於事件驅動,那麼此類的作用就是用戶傳遞和處理事件。我們知道XML文檔中的Element是必須要成對出現的,那麼這就有兩個事件需要觸發,開始節點和結束節點。如果解析的時候,parse(InputStream is, DefaultHandler dh)方法參數2需要傳入DefaultHandler對象,所以在解析的時候,節點處理類dh會解析到那個節點,就出發哪一個方法。具體請查看

但是這裏我們需要的是手動觸發事件。所以就需要獲取TransformerHandler類對象,此對象可以手動觸發事件。在解析使用到的DefaultHandler類和TransformerHandler類具有同樣的父類,所以都是可以觸發事件的。

獲取轉換類Transformer

Transformer類其實是用來處理XML文檔,並且將它寫入到特定的容器中,有以下功能:

設置文檔編碼方式OutputKeys.ENCODING,

OutputKeys.VERSION版本

是否寫出XML文檔的說明OutputKeys.OMIT_XML _DECLARATION(取值:yes|no)

文檔是否獨立STANDALONE(取值:yes|no):no 表示這個 XML 文檔不是獨立的而是依賴於外部所定義的一個 DTD,yes 表示這個 XML 文檔是自包含的(self-contained)。INDENT設置縮進,yes|no。

設置輸出

上面說到了,我們如何轉換,但是轉換的結果應該要怎麼處理呢?TransformerHandler類給我們提供了StreamResult()方法,設置輸出流。

代碼如下:

            public void setResult(Result result)

StreamResult實現了Result接口。Result類的作用是建立一個轉換結果樹。我們要使用的就是其子類。
其構造方法有5個:

public StreamResult()
public StreamResult(OutputStream outputStream) 
public StreamResult(Writer writer)
public StreamResult(String systemId)
public StreamResult(File f)

傳入的參數是結果的接收對象。
無參構造方法,可以調用set方法設置輸出接收對象。OutputStream是輸出流,比如用文件打開一個輸出流,將轉換後的數據寫入到文件在,因爲輸出流不能夠在程序中得到輸出的內容,所以可以使用Writer,創建一個Result,然後在,利用getBuffer()方法或者totring()方法得到結果,再返回出去。或者是一個File文件參數,先寫入本地,然後需要的時候再拿出來。

開始創建XML內容了

前面所做的都是準備工作,準備文件的說明和輸出等,下面就是開始真正的進行建立文檔內容了。一個節點有開始,那麼必須有結束,相同節點是成對出現的。如果不是成對出現,那麼就會報不匹配的錯誤。

startDocument()/endDocument()

開始和結束文檔節點要觸發的方法。所有節點都需要在這兩個方法之間完成。

transformerHandler.startDocument();
transformerHandler.endDocument();

startElement()/endElement()

public void startElement (String uri, String localName,
                  String qName, Attributes atts)
public void endElement (String uri, String localName,
                String qName)

參數解釋:

參數1:命名空間的URI。

參數2:節點名稱。

參數3:節點的全限定名。

參數Attributes:此節點的屬性。例如< user>節點有id屬性,那麼就需要設置。

設置屬性的方法利用的是Attributes接口的實現類AttributesImpl。AttributesImpl類提供了一系列的方法,比如:

//得到屬性的數量
public int getLength ()

//獲取命名空間
public String getURI (int index)

//得到本地屬性名
public String getLocalName (int index)

//獲取屬性全限定名
public String getQName (int index)

//獲取屬性類型
public String getType (int index)

//獲取第幾個屬性的值
public String getValue (int index)

//根據命名空間和本地屬性名獲取位置
public int getIndex (String uri, String localName)

//清除該節點所有屬性
public void clear ()

//根據下標移除屬性
public void removeAttribute (int index)

//設置節點的URI
public void setURI (int index, String uri)

//設置屬性名
public void setLocalName (int index, String localName)

//設置屬性全限定名
public void setQName (int index, String qName)

//設置屬性類型
public void setType (int index, String type)

//設置屬性的值
public void setValue (int index, String value)

其中比較要關注的是:

//新加一個屬性
addAttribute (String uri, String localName, String qName,
                  String type, String value)

//設置屬性
public void setAttribute (int index, String uri, String localName,
                  String qName, String type, String value)

舉例說明:
比如我要在節點user裏面加入一個id的屬性,最終是這樣的效果< user id=”1”>

代碼實例:

AttributesImpl attributes = new AttributesImpl();
//需要先清除屬性,以免上一個節點屬性影響本節點屬性
attributes.clear();
                attributes.addAttribute("", "id", "id", "int", 
//最後一個參數設置屬性對象       
String.valueOf(userBean.getId()));
                transformerHandler.startElement("", "user", "user", attributes);

characters()

上面講完了如何創建節點和給節點加入屬性。如果有一個節點下面不再包含子節點,而是一個節點值了。那麼該如何來設置值呢?

調用的是public void characters (char ch[], int start, int length);方法

參數1:char字符數組;參數2:數組開始的下邊;參數3:開始的長度;

這個方法比較簡單哈,只要拿到對應實體的屬性值,然後直接設置進去就OK了。

最後我們來看一下中的代碼:

對應的實體類UserBean.java

public class UserBean implements Serializable {
    //串行化版本統一標識符
    private static final long serialVersionUID = 1L;

    private int id;
    private String userName;
    private String password;
    private int age;
    //省略setter和getter方法

}

buildXmlBySax()方法:

/**
     * SAX生成XML
     *
     * @param userBeanList 實體類集合
     * @param outputStream 輸出流
     */
    public static void buildXmlBySax(List<UserBean> userBeanList, OutputStream outputStream) {
        try {
            SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
            TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();
            Transformer transformer = transformerHandler.getTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            Result result = new StreamResult(outputStream);
            transformerHandler.setResult(result);

            transformerHandler.startDocument();
            AttributesImpl attributes = new AttributesImpl();
            transformerHandler.startElement("", "users", "users", attributes);
            for (UserBean userBean : userBeanList) {
                attributes.clear();
                attributes.addAttribute("", "id", "id", "", String.valueOf(userBean.getId()));
                transformerHandler.startElement("", "user", "user", attributes);

                attributes.clear();

                transformerHandler.startElement("", "userName", "userName", attributes);
                transformerHandler.characters(userBean.getUserName().toCharArray(), 0, userBean.getUserName().length());
                transformerHandler.endElement("", "userName", "userName");

                transformerHandler.startElement("", "password", "password", attributes);
                transformerHandler.characters(userBean.getPassword().toCharArray(), 0, userBean.getPassword().length());
                transformerHandler.endElement("", "password", "password");

                transformerHandler.startElement("", "age", "age", attributes);
                transformerHandler.characters(String.valueOf(userBean.getAge()).toCharArray(), 0, String.valueOf(userBean.getAge()).length());
                transformerHandler.endElement("", "age", "age");

                transformerHandler.endElement("", "user", "user");
            }
            transformerHandler.endElement("", "users", "users");
            transformerHandler.endDocument();
        } catch (TransformerConfigurationException | SAXException e) {
            e.printStackTrace();
        }
    }

最後在activity裏面調用此方法,這裏我獲取了設備內部公共存儲空間的,並且建立一個文件buildusersbysax.xml,文件名最好小寫,別問我爲什麼,因爲可以跨平臺。

代碼如下:

//文件夾
File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);

//讀取本地XML文件
InputStream inputStream = getResources().openRawResource(R.raw.users);

//解析本地XML文件
List<UserBean> userBeanList = XmlUtil.parseXmlByPull(inputStream);

//最後在根據實體集合,生成XML文件
try {
            String outPutPath = file.getAbsolutePath() + "buildusersbysax.xml";
            File outFile = new File(outPutPath);
            if (outFile.exists()){
                outFile.delete();
                outFile.createNewFile();
            }else {
                outFile.createNewFile();
            }
            CommonLog.logInfo("path:" + outFile.getAbsolutePath());
            FileOutputStream fileOutputStream = new FileOutputStream(outFile);
            XmlUtil.buildXmlBySax(userBeanList, fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

下面是運行生成的XML文件。截圖

總結

主要講解了如何獲取SAXTransformerFactory工廠類,建立TransformerHandler轉換的處理類,然後在獲取Transformer轉換類,以及每個類的作用和聯繫。最後詳細說明了下如何觸發生成文檔需要的每個節點,包括開始建立文檔(包括建立XML文件的說明),開始建立節點並且設置節點屬性和設置節點值。最後千萬別忘了,每個節點都是要成對出現,並且都需要關閉。
關於如何確保關閉節點,我們可以start一個節點,立馬就寫一個關閉的方法,然後在兩個中間寫我們需要的代碼。

總的來說,sax加你文檔並不難。如果有更加複雜的結構,只要理清了結構,那就都不是問題了。

以上就是所有內容,如有任何問題,請及時與我聯繫,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章