理解SAX解析xml的工作原理
當XMLReader讀到<POEM>標籤時,就會呼叫ContentHandler.startDocument()方法,並把標籤名POEM作為引數傳遞過去。在你實現的startElement()方法中需要做相應的動作,以處理當<POEM>出現時應該做的事情。各個事件隨著解析的過程(也就是文件讀入的過程)一個個順序的被丟擲,相應的方法也會被順序的呼叫,最後,當解析完成,方法都被呼叫後,對文件的處理也就完成了。下面的這個表,列出了在解析上面的那個XML檔案的時候,順序被呼叫的方法。
方法回撥過程:
{文件開始} STARTDOCUMENT()
<POEM> STARTELEMENT(NULL,"POEM",NULL,{ATTRIBUTES})
" " CHARACTERS("<POEM> ...", 6, 1)
<AUTHOR> STARTELEMENT(NULL,"AUTHOR",NULL,{ATTRIBUTES})
"OGDEN NASH" CHARACTERS("<POEM> ...", 15, 10)
</AUTHOR> ENDELEMENT(NULL,"AUTHOR",NULL)
" " CHARACTERS("<POEM> ...", 34, 1)
<TITLE> STARTELEMENT(NULL,"TITLE",NULL,{ATTRIBUTES})
"FLEAS" CHARACTERS("<POEM> ...", 42, 5)
</TITLE> ENDELEMENT(NULL,"TITLE",NULL)
" " CHARACTERS("<POEM> ...", 55, 1)
<LINE> STARTELEMENT(NULL,"LINE",NULL,{ATTRIBUTES})
"ADAM" CHARACTERS("<POEM> ...", 62, 4)
</LINE> ENDELEMENT(NULL,"LINE",NULL)
" " CHARACTERS("<POEM> ...", 67, 1)
</POEM> ENDELEMENT(NULL,"POEM",NULL)
{文件結束} ENDDOCUMENT()
ContentHandler實際上是一個介面,當處理特定的XML檔案的時候,就需要為其建立一個實現了ContentHandler的類來處理特定的事件,這個實際上就是SAX處理XML檔案的核心。下面是幾個主要的方法:
void characters(char[] ch, int start, int length):這個方法用來處理在XML檔案中讀到字串,它的引數是一個字元陣列,以及讀到的這個字串在這個陣列中的起始位置和長度,我們可以很容易的用String類的一個構造方法來獲得這個字串的String類:String charencontered=new String(ch,start,length)。
void startDocument():當遇到文件的開頭的時候,呼叫這個方法,可以在其中做一些預處理的工作。
void endDocument():當文件結束的時候,呼叫這個方法,可以在其中做一些善後的工作。
void startElement(String namespaceuri, String localname, String qname, Attributes atts):當讀到一個開始標籤的時候,會觸發這個方法。在SAX1.0版本中並不支援名域,而在新的2.0版本中提供了對名域的支援,這兒引數中的namespaceuri
注意:SAX中一個重要的特點就是它的流式處理,在遇到一個標籤的時候,它並不會紀錄下以前所碰到的標籤,也就是說,在startElement()方法中,所有你所知道的資訊,就是標籤的名字和屬性,至於標籤的巢狀結構,上層標籤的名字,是否有子元屬等等其它與結構相關的資訊,都是不得而知的,都需要你的程式來完成。這使得SAX在程式設計處理上沒有DOM來得那麼方便。
void endElement(String namespaceuri, String localname, String qname):和startElement
沿用講DOM的時候使用的那個文件例子,首先,我們先看一個簡單一些的應用,我們希望能夠統計一下XML檔案中各個標籤出現的次數。這個例子很簡單,但是足以闡述SAX程式設計的基本思路了。
import org.xml.sax.helpers.defaulthandler;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.*;
import java.io.*;
//建立一個繼承於defaulthandler的類
public class saxcounter extends defaulthandler {
private hashtable tags; //這個hashtable用來記錄tag出現的次數
// 處理文件前的工作
public void startdocument() throws saxexception {
tags = new hashtable();//初始化hashtable
}
//對每一個開始元屬進行處理
public void startelement(string namespaceuri, string localname,
string rawname, attributes atts) throws saxexception
{
string key = localname;
……
我們來看看這段程式作了些什麼。在main()方法中,主要做的就是建立解析器,然後解析文件。實際上,在這兒建立SaxParser物件的時候,為了使程式程式碼於具體的解析器無關,使用了同DOM中一樣的設計技巧:通過一個SaxParserFactory類來建立具體的SaxParser物件,這樣,當需要使用不同的解析器的時候,要改變的,只是一個環境變數的值,而程式的程式碼可以保持不變。這就是工廠方法模式的思想。
不過在這兒還有一點點要注意的地方,就是SaxParser類和XmlReader類之間的關係。實際上SaxParser是JAXP中對XmlReader的一個封裝類,而XmlReader是定義在SAX2.0種的一個用來解析文件的介面。你可以同樣的呼叫SaxParser或者XmlReader中的parser()方法來解析文件,效果是完全一樣的。不過在SaxParser中的parser()方法接受更多的引數,可以對不同的XML文件資料來源進行解析,因而使用起來要比XmlReader要方便一些。
下面我們要實現的功能,在DOM的例子中已經有實現了,就是從XML文件中讀出內容並格式化輸出。
前面說過,當遇到一個開始標籤的時候,在startElement()方法中,我們並不能夠得到這個標籤在XML文件中所處的位置。這在處理XML文件的時候是個大麻煩,因為在XML中標籤的語義,有一部分是由其所處的位置所決定的。而且在一些需要驗證文件結構的程式中,這更是一個問題。當然,沒有解決不了的問題了,我們可以使用一個棧來實現對文件結構的紀錄。
棧的特點是先進先出,我們現在的想法是,在startElement()方法中用PUSH將這個標籤的名字新增到棧中,在endElement()方法中在把它pop出來。我們知道對一個結構良好的XML而言,其巢狀結構是完備的,每一個開始標籤總會對應一個結束標籤,而且不會出現標籤巢狀之間的錯位。因而,每一次STARTELEMENT()方法的呼叫,必然會對應一個endElement()方法的呼叫,這樣push和pop也是成對出現的,我們只需要分析棧的結構,就可以很容易的知道當前標籤所處在文件結構中的位置了。
public class SaxReader extends DefaultHandler {
java.util.Stack tags=new java.util.stack();
……
在這兒雖然沒有使用到棧的分析,但實際上棧的分析是一件很容易的事情,應為java.util.Stack繼承了java.util.Vector類,而且stack中的元素是按棧的結構由底至上排列的,因個,我們可以使用vector類的size()方法來得到stack的元素個數,還可以使用vector的get(int index)方法來得到具體的每一個元屬。實際上,如果把stack的元素從底向上逐一排列出來,我們就得到了從XML根節點到當前節點的一條唯一的路徑,有了這條路徑的資訊,文件的結構就在清楚不過了。
3.總結
到目前為止,我們已經掌握了對於XML程式設計的兩大利器:DOM和SAX,也知道了該如何在一個JAVA程式中使用它們。DOM程式設計相對簡單,但是速度比較慢,佔用記憶體多,而SAX程式設計複雜一些,但是速度快,佔用記憶體少。所以,我們應該根據不同的環境選擇使用不同的方法。大部分的XML應用基本都可以用它們來解決。需要特別說明的是,DOM和SAX其實都是語言無關的,並非JAVA所獨有,也就是說,只要有相應的語言實現,DOM和SAX可以應用在任何面向物件的語言中。
https://my.oschina.net/chape/blog/262434=============================================================================================
程式碼驗證:
public class MainActivity extends AppCompatActivity { Button _button=null; TextView _textView=null; class CMyHandler extends Handler{ @Override public void handleMessage(Message msg) { super.handleMessage(msg); String _strData=msg.getData().getString("data1"); _textView.setText(_strData); } } class CMyXmlHandler extends DefaultHandler{ public List<String> _listStr=null; @Override public void startDocument() throws SAXException { // super.startDocument(); _listStr=new ArrayList<>(); } @Override public void endDocument() throws SAXException { // super.endDocument(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //super.startElement(uri, localName, qName, attributes); _listStr.add("start:"+localName+" "); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //super.endElement(uri, localName, qName); _listStr.add("end:"+localName+" "); } @Override public void characters(char[] ch, int start, int length) throws SAXException { // super.characters(ch, start, length); String _strTem=new String(ch,start,length); _listStr.add("ch:"+_strTem+" "); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // _button= (Button) findViewById(R.id.main_button1); _textView= (TextView) findViewById(R.id.main_textView1); final CMyHandler _handler=new CMyHandler(); _button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Thread(new Runnable() { @Override public void run() { try { URL _url=new URL("http://cloud.bmob.cn/0906a62b462a3082/getXml"); try { HttpURLConnection _httpUrlConnection= (HttpURLConnection) _url.openConnection(); _httpUrlConnection.setDoInput(true); _httpUrlConnection.setDoOutput(true); _httpUrlConnection.setRequestMethod("POST"); _httpUrlConnection.setRequestProperty("Accept-CharSet","UTF-8"); _httpUrlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); _httpUrlConnection.connect(); InputStream _inputStream= _httpUrlConnection.getInputStream(); /* BufferedReader _bufferReader=new BufferedReader(new InputStreamReader(_inputStream)); StringBuilder _stringBuilder=new StringBuilder(); String _strTem=""; while((_strTem=_bufferReader.readLine())!=null){ _stringBuilder.append(_strTem); } Bundle _bundle=new Bundle(); _bundle.putString("data",_stringBuilder.toString()); Message _message=new Message(); _message.setData(_bundle); _handler.sendMessage(_message);*/ SAXParserFactory _saxParserFactory=SAXParserFactory.newInstance(); try { SAXParser _parser= _saxParserFactory.newSAXParser(); CMyXmlHandler _XMLhandler=new CMyXmlHandler(); _parser.parse(_inputStream,_XMLhandler); _inputStream.close(); Bundle _bundle=new Bundle(); String _strData1=""; for (int i=0;i<_XMLhandler._listStr.size();i++){ _strData1+=_XMLhandler._listStr.get(i); } _bundle.putString("data1",_strData1); Message _message=new Message(); _message.setData(_bundle); _handler.sendMessage(_message); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); } } }).start(); } }); // } }
=============================================================================================
參考:使用SAX解析XML檔案