1. 程式人生 > >理解SAX解析xml的工作原理

理解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

就是名域,localname是標籤名,qname是標籤的修飾字首,當沒有使用名域的時候,這兩個引數都未NULL。而atts是這個標籤所包含的屬性列表。通過ATTS,可以得到所有的屬性名和相應的值。

    注意: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檔案