Android之SAX生成XML
文章鏈接:http://blog.csdn.net/qq_16628781/article/details/70174654
知識點:
- SAXTransformerFactory類及其對象說明;
- TransformerHandler類及其對象說明;
- Transformer類及其對象說明;
- SAX生成XML實例講解;
- 新名詞記錄{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加你文檔並不難。如果有更加複雜的結構,只要理清了結構,那就都不是問題了。
以上就是所有內容,如有任何問題,請及時與我聯繫,謝謝!