上一章寫了如何使用DOM來解析和生成xml
接下來這章將講解SAX方式解析和生成xml
下面我們看下DOM和SAX的優缺點分析
- DOM(文件對象模型)解析:解析器讀入整個文檔,然後構建一個駐留內存的樹結構,然後代碼就可以根據DOM接口來操作這個樹結構了。
- 優點:整個文檔讀入內存,方便操作:支持修改、刪除和重現排列等多種功能。
- 缺點:將整個文檔讀入內存中,保留了過多的不需要的節點,浪費內存和空間。
- 使用場合:一旦讀入文檔,還需要多次對文檔進行操作,並且在硬件資源充足的情況下(內存,CPU)。
- 爲了解決DOM解析存在的問題,就出現了SAX解析。其特點爲:
- 優點:不用實現調入整個文檔,佔用資源少。尤其在嵌入式環境中,如android,極力推薦使用SAX解析。
- 缺點:不像DOM解析一樣將文檔長期駐留在內存中,數據不是持久的。如果事件過後沒有保存數據,數據就會丟失。
- 使用場合:機器有性能限制。
一、SAX解析XML
下面是SAX實現實體解析的步驟
- //下面使用XMLReader 來解析
- (一)第一步:新建一個工廠類SAXParserFactory,代碼如下:
- SAXParserFactory factory = SAXParserFactory.newInstance();
- (二)第二步:讓工廠類產生一個SAX的解析類SAXParser,代碼如下:
- SAXParser parser = factory.newSAXParser();
- (三)第三步:從SAXPsrser中得到一個XMLReader實例,代碼如下:
- XMLReader reader = parser.getXMLReader();
- (四)第四步:把自己寫的handler註冊到XMLReader中,一般最重要的就是ContentHandler,代碼如下:
- reader.setContentHandler(this);
- (五)第五步:將一個xml文檔或者資源變成一個java可以處理的InputStream流後,解析正式開始,代碼如下:
- reader.parse(new InputSource(is));
- //下面使用SAXParser來解析
- (一)第一步:新建一個工廠類SAXParserFactory,代碼如下:
- SAXParserFactory factory = SAXParserFactory.newInstance();
- (二)第二步:讓工廠類產生一個SAX的解析類SAXParser,代碼如下:
- SAXParser parser = factory.newSAXParser();
- (三)第三步:將一個xml文檔或者資源變成一個java可以處理的InputStream流後,解析正式開始,代碼如下:
- parser.parse(is,this);
估計大家都看到了ContentHandler ,下面具體的講下
解析開始之前,需要向XMLReader/SAXParser 註冊一個ContentHandler,也就是相當於一個事件監聽器,在ContentHandler中定義了很多方法
- //設置一個可以定位文檔內容事件發生位置的定位器對象
- public void setDocumentLocator(Locator locator)
- //用於處理文檔解析開始事件
- public void startDocument()throws SAXException
- //處理元素開始事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱,屬性類表等信息
- public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException
- //處理元素結束事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱等信息
- public void endElement(String namespacesURI , String localName , String qName) throws SAXException
- //處理元素的字符內容,從參數中可以獲得內容
- public void characters(char[] ch , int start , int length) throws SAXException
順便介紹下XMLReader中的方法。
- //註冊處理XML文檔解析事件ContentHandler
- public void setContentHandler(ContentHandler handler)
- //開始解析一個XML文檔
- public void parse(InputSorce input) throws SAXException
下面是流程圖
大概的講的差不多了 接下來開始講解解析的步驟
我們還是用上一章的代碼
首先 我們創建一個Person類 用來存儲用戶的信息
- package com.example.demo;
- import java.io.Serializable;
- public class Person implements Serializable {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- private String _id;
- private String _name;
- private String _age;
- public String get_id() {
- return _id;
- }
- public void set_id(String _id) {
- this._id = _id;
- }
- public String get_name() {
- return _name;
- }
- public void set_name(String _name) {
- this._name = _name;
- }
- public String get_age() {
- return _age;
- }
- public void set_age(String _age) {
- this._age = _age;
- }
- }
接下來 我們要實現一個ContentHandler 用來解析XML
實現一個ContentHandler 一般需要下面幾個步驟
- 1、聲明一個類,繼承DefaultHandler。DefaultHandler是一個基類,這個類裏面簡單實現了一個ContentHandler。我們只需要重寫裏面的方法即可。
- 2、重寫 startDocument() 和 endDocument(),一般將正式解析之前的初始化放到startDocument()裏面,收尾的工作放到endDocument()裏面。
- 3、重寫startElement(),XML解析器遇到XML裏面的tag時就會調用這個函數。經常在這個函數內是通過對localName的值進行判斷而操作一些數據。
- 4、重寫characters()方法,這是一個回調方法。解析器執行完startElement()後,解析節點的內容後就會執行這個方法,並且參數ch[]就是節點的內容。
- 5、重寫endElement()方法,這個方法與startElement()相對應,解析完一個tag節點後,執行這個方法,解析一個tag後,調用這個處理還原和清除相關信息
首先 新建一個類 繼承DefaultHandler 並重寫以下幾個方法
- public class SAX_parserXML extends DefaultHandler {
- /**
- * 當開始解析xml文件的聲明的時候就會觸發這個事件, 可以做一些初始化的工作
- * */
- @Override
- public void startDocument() throws SAXException {
- // TODO Auto-generated method stub
- super.startDocument();
- }
- /**
- * 當開始解析元素的開始標籤的時候,就會觸發這個事件
- * */
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- // TODO Auto-generated method stub
- super.startElement(uri, localName, qName, attributes);
- }
- /**
- * 當讀到文本元素的時候要觸發這個事件.
- * */
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- // TODO Auto-generated method stub
- super.characters(ch, start, length);
- }
- /**
- * 當讀到結束標籤的時候 就會觸發這個事件
- * */
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- // TODO Auto-generated method stub
- super.endElement(uri, localName, qName);
- }
- }
首先 我們創建一個list 用來保存解析出來的person數據
- List<Person> persons;
但是?在哪裏初始化呢?我們可以在startDocument()裏面初始化,因爲當開始解析xml文件的聲明的時候就會觸發這個事件所以放在這裏比較合適
- /**
- * 當開始解析xml文件的聲明的時候就會觸發這個事件, 可以做一些初始化的工作
- * */
- @Override
- public void startDocument() throws SAXException {
- // TODO Auto-generated method stub
- super.startDocument();
- // 初始化list
- persons = new ArrayList<Person>();
- }
接下來 就要開始解析了
- /**
- * 當開始解析元素的開始標籤的時候,就會觸發這個事件
- * */
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- // TODO Auto-generated method stub
- super.startElement(uri, localName, qName, attributes);
- // 如果讀到是person標籤 開始存儲
- if (localName.equals("person")) {
- person = new Person();
- person.set_id(attributes.getValue("id"));
- }
- curNode = localName;
- }
上面的代碼中 localName表示當前解析到的元素名
- //步驟
- //1.判斷是否是person元素
- //2.創建新的Person對象
- //3.獲取id 添加到Person對象中
curNode 用來保存當前的元素名 在characters中會使用到
- /**
- * 當讀到文本元素的時候要觸發這個事件.
- * */
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- // TODO Auto-generated method stub
- super.characters(ch, start, length);
- if (person != null) {
- //取出目前元素對應的值
- String txt = new String(ch, start, length);
- //判斷元素是否是name
- if (curNode.equals("name")) {
- //將取出的值添加到person對象
- person.set_name(txt);
- } else if (curNode.equals("age")) {
- person.set_age(txt);
- }
- }
- }
接下來是介紹標籤結束的時候需要做的事情
- /**
- * 當讀到結束標籤的時候 就會觸發這個事件
- * */
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- // TODO Auto-generated method stub
- super.endElement(uri, localName, qName);
- // 如果是 並且person不爲空,添加到list
- if (localName.equals("person") && person != null) {
- persons.add(person);
- person = null;
- }
- curNode = "";
- }
解析的事情結束了 大概流程就是
- 1.一個元素開始時 會調用startElement方法
- 2.接下來會調用到characters方法,可以用來獲取元素的值
- 3.一個元素結束時 會調用到endElement方法
解析結束之後 我們需要寫一個方法 用來獲取解析後保存的list
- public List<Person> ReadXML(InputStream is) {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- try {
- SAXParser parser = factory.newSAXParser();
- // 第一種方法
- // parser.parse(is, this);
- // 第二種方法
- XMLReader reader = parser.getXMLReader();
- reader.setContentHandler(this);
- reader.parse(new InputSource(is));
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- }
- return persons;
- }
上面的代碼就不解釋了 只要將inputStream對象傳入 就可以解析出內容
看完了代碼,我來給出完整的代碼
- package com.example.demo.Utils;
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.List;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.xml.sax.Attributes;
- import org.xml.sax.InputSource;
- import org.xml.sax.SAXException;
- import org.xml.sax.XMLReader;
- import org.xml.sax.helpers.DefaultHandler;
- import com.example.demo.Person;
- public class SAX_parserXML extends DefaultHandler {
- List<Person> persons;
- Person person;
- // 當前節點
- String curNode;
- public List<Person> ReadXML(InputStream is) {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- try {
- SAXParser parser = factory.newSAXParser();
- // 第一種方法
- // parser.parse(is, this);
- // 第二種方法
- XMLReader reader = parser.getXMLReader();
- reader.setContentHandler(this);
- reader.parse(new InputSource(is));
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- }
- return persons;
- }
- /**
- * 當開始解析xml文件的聲明的時候就會觸發這個事件, 可以做一些初始化的工作
- * */
- @Override
- public void startDocument() throws SAXException {
- // TODO Auto-generated method stub
- super.startDocument();
- // 初始化list
- persons = new ArrayList<Person>();
- }
- /**
- * 當開始解析元素的開始標籤的時候,就會觸發這個事件
- * */
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
- // TODO Auto-generated method stub
- super.startElement(uri, localName, qName, attributes);
- // 如果讀到是person標籤 開始存儲
- if (localName.equals("person")) {
- person = new Person();
- person.set_id(attributes.getValue("id"));
- }
- curNode = localName;
- }
- /**
- * 當讀到文本元素的時候要觸發這個事件.
- * */
- @Override
- public void characters(char[] ch, int start, int length)
- throws SAXException {
- // TODO Auto-generated method stub
- super.characters(ch, start, length);
- if (person != null) {
- // 取出目前元素對應的值
- String txt = new String(ch, start, length);
- // 判斷元素是否是name
- if (curNode.equals("name")) {
- // 將取出的值添加到person對象
- person.set_name(txt);
- } else if (curNode.equals("age")) {
- person.set_age(txt);
- }
- }
- }
- /**
- * 當讀到結束標籤的時候 就會觸發這個事件
- * */
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
- // TODO Auto-generated method stub
- super.endElement(uri, localName, qName);
- // 如果是person結尾 並且person不爲空,添加到list
- if (localName.equals("person") && person != null) {
- persons.add(person);
- person = null;
- }
- curNode = "";
- }
- }
寫個方法調用下這個類
- List<Person> persons = new SAX_parserXML().ReadXML(is);
- StringBuffer buffer = new StringBuffer();
- for (int i = 0; i < persons.size(); i++) {
- Person person =persons.get(i);
- buffer.append("id:" + person.get_id() + " ");
- buffer.append("name:" + person.get_name() + " ");
- buffer.append("age:" + person.get_age() + "\n");
- }
- Toast.makeText(activity, buffer, Toast.LENGTH_LONG).show();
如果你看到下面的界面 說明解析成功了~
解析的問題 就講到這裏 如果有缺少的 有問題的 可以留言 會在博客中補充
二、SAX生成XML
Sax方式創建XML,應用了標準xml構造器 javax.xml.transform.sax.TransformerHandler 事件來創建 XML 文檔
首先,SAXTransformerFactory.newInstance() 創建一個工廠實例 factory
接着,factory.newTransformerHandler() 獲取 TransformerHandler 的 handler 對象
然後,通過 handler 事件創建handler.getTransformer()、 handler.setResult(result),以及 startDocument()、startElement、characters、endElement、endDocument()等
寫代碼之前 我們來講下幾個會用到的類
SAXTransformerFactory
此類擴展了 TransformerFactory 以提供特定於 SAX 的工廠方法。它提供兩種類型的 ContentHandler,一種用於創建 Transformer,另一種用於創建 Templates 對象。
如果應用程序希望設置轉換期間所使用的 XMLReader 的 ErrorHandler 或 EntityResolver,那麼它應使用 URIResolver 來返回提供了(通過 getXMLReader)對 XMLReader 引用的 SAXSource。
newTemplatesHandler() |
獲取能夠將 SAX ContentHandler 事件處理爲 Templates 對象的 TemplatesHandler 對象。 |
newTransformerHandler() |
獲取能夠將 SAX ContentHandler 事件處理爲 Result 的 TransformerHandler 對象。 |
newTransformerHandler(Source src) |
基於參數所指定的轉換指令,獲取能夠將 SAX ContentHandler 事件處理爲 Result 的 TransformerHandler 對象。 |
newTransformerHandler(Templates templates) |
基於 Templates 參數,獲取能夠將 SAX ContentHandler 事件處理爲 Result 的 TransformerHandler 對象。 |
newXMLFilter(Source src) |
創建使用給定 Source 作爲轉換指令的 XMLFilter |
newXMLFilter(Templates templates) |
基於 Templates 參數,創建 XMLFilter |
TransformerHandler
偵聽 SAX ContentHandler 解析事件,並將它們轉換爲 Result 的 TransformerHandler
getTransformer() |
獲取與此處理程序關聯的 Transformer ,用於設置參數和輸出屬性。 |
setResult(Result result) |
設置與用於轉換的此 TransformerHandler 關聯的 Result 。 |
首先 我們來創建一個工程實例
- SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory
- .newInstance();
接下來 獲取handler對象
- TransformerHandler handler = factory.newTransformerHandler();
通過handler.getTransformer()獲取Transformer對象 ,然後設置xml的屬性
- // 獲取與此處理程序關聯的 Transformer,用於設置參數和輸出屬性。
- Transformer info = handler.getTransformer();
- // 是否自動添加額外的空白
- info.setOutputProperty(OutputKeys.INDENT, "yes");
- // 設置字符編碼
- info.setOutputProperty(OutputKeys.ENCODING, "utf-8");
- info.setOutputProperty(OutputKeys.VERSION, "1.0");
創建一個StreamResult對象用來保存創建的xml
- StringWriter stringWriter = new StringWriter();
- // 創建一個StreamResult對象用來保存創建的xml
- StreamResult result = new StreamResult(stringWriter);
- handler.setResult(result);
下面開始處理生成xml,先創建測試數據
- private List<Person> getTestValues() {
- List<Person> persons = new ArrayList<Person>();
- Person person = new Person();
- person.set_id("23");
- person.set_name("李磊");
- person.set_age("30");
- persons.add(person);
- person = new Person();
- person.set_id("20");
- person.set_name("韓梅梅");
- person.set_age("25");
- persons.add(person);
- return persons;
- }
來看下最後生成的xml是什麼樣子的?
- <?xml version="1.0" encoding="UTF-8"?>
- <persons>
- <person id="23">
- <name>李磊</name>
- <age>30</age>
- </person>
- <person id="20">
- <name>韓梅梅</name>
- <age>25</age>
- </person>
- </persons>
調用startDocument()表示開始 ,調用endDocument()表示結束,所以 寫代碼的時候直接把兩個都寫上,以防萬一最後忘記
- // 開始xml
- handler.startDocument();
- /*代碼寫在start和end之間*/
- // 結束xml
- handler.endDocument();
接下來開始寫xml 第一步 創建根節點persons,同樣的,接下來的代碼卸載start和end之間
- AttributesImpl impl = new AttributesImpl();
- impl.clear();
- handler.startElement("", "", "persons", impl);
- //創建要對應結束 所以寫完start 馬上補充end
- handler.endElement("", "", "persons");
創建完根節點之後 開始創建person元素 person中含有一個id屬性
- impl.clear();
- impl.addAttribute("", "", "id", "", person.get_id());
- handler.startElement("", "", "person", impl);
- /*在這裏創建name和age元素*/
- handler.endElement("", "", "person");
下面創建name和age元素
- impl.clear();
- handler.startElement("", "", "name", impl);
- String txt = person.get_name();
- handler.characters(txt.toCharArray(), 0, txt.length());
- handler.endElement("", "", "name");
- impl.clear();
- handler.startElement("", "", "age", impl);
- txt = person.get_age();
- handler.characters(txt.toCharArray(), 0, txt.length());
- handler.endElement("", "", "age");
看下完整代碼
- public String createXML() {
- // TODO Auto-generated method stub
- StringWriter stringWriter = new StringWriter();
- // 創建測試數據
- List<Person> persons = getTestValues();
- try {
- // 創建工廠
- SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory
- .newInstance();
- TransformerHandler handler = factory.newTransformerHandler();
- Transformer info = handler.getTransformer();
- // 是否自動添加額外的空白
- info.setOutputProperty(OutputKeys.INDENT, "yes");
- // 設置字符編碼
- info.setOutputProperty(OutputKeys.ENCODING, "utf-8");
- info.setOutputProperty(OutputKeys.VERSION, "1.0");
- // 保存創建的xml
- StreamResult result = new StreamResult(stringWriter);
- handler.setResult(result);
- // 開始xml
- handler.startDocument();
- AttributesImpl impl = new AttributesImpl();
- impl.clear();
- handler.startElement("", "", "persons", impl);
- for (int i = 0; i < persons.size(); i++) {
- Person person = persons.get(i);
- impl.clear();
- impl.addAttribute("", "", "id", "", person.get_id());
- handler.startElement("", "", "person", impl);
- impl.clear();
- handler.startElement("", "", "name", impl);
- String txt = person.get_name();
- handler.characters(txt.toCharArray(), 0, txt.length());
- handler.endElement("", "", "name");
- impl.clear();
- handler.startElement("", "", "age", impl);
- txt = person.get_age();
- handler.characters(txt.toCharArray(), 0, txt.length());
- handler.endElement("", "", "age");
- handler.endElement("", "", "person");
- }
- handler.endElement("", "", "persons");
- // 結束xml
- handler.endDocument();
- return stringWriter.toString();
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- }
- return null;
- }
調用方法 查看返回的String是不是xml格式的 如果是的 表示調用成功了
我要講解了 也就結束了
個人語言水平有限 不懂的 可以留言 我修改
下面一章會講解pull解析和生成xml