Android系列--DOM、SAX、Pull解析XML

來源:互聯網
上載者:User

標籤:reference   click   表情   dev   內容   重要   ted   設定   ttext   

一、DOM解析XML

我們首先來看看DOM(Document Object Model)這種方式解析xml,通過DOM解析xml在j2ee開發中非常的常見,它將整個xml看成是一個樹狀的結構,在解析的時候,會將整個xml檔案載入到我們的記憶體當中,然後通過DOM提供的API來對我們的xml資料進行解析,這種方式解析xml非常的方便,並且我們可以通過某個節點訪問到其兄弟或者是父類、子類節點。那麼通過DOM來解析xml的步驟是怎樣的呢?

1.首先通過DocumentBuilderFactory這個類來構建一個解析工廠類,通過newInstance()的方法可以得到一個DocumentBuilderFactory的對象。

2.通過上面的這個工廠類建立一個DocumentBuilder的對象,這個類就是用來對我們的xml文檔進行解析,通過DocumentBuilderFactory的newDocumentBuilder()方法

3.通過建立好的 DocumentBuilder 對象的 parse(InputStream) 方法就可以解析我們的xml文檔,然後返回的是一個Document的對象,這個Document對象代表的就是我們的整個xml文檔。

4.得到了整個xml的Document對象後,我們可以獲得其下面的各個元素節點(Element),同樣每個元素節點可能又有多個屬性(Attribute),根據每個元素節點我們又可以遍曆該元素節點下面的子節點等等。

在這裡要說明一下,在DOM的API當中,Node這個介面代表了我們整個的DOM對象的最初資料類型,它代表了整個document樹中的每一個單一節點。所有實現了Node這個介面的對象都可以處理其孩子節點,當然,並不是每個節點都有children,例如TextNode(文本節點),通過Node的 nodeName、nodeValue、attributes這三個屬性,我們可以很方便的得到每個Node節點的節點名字、節點的值、節點屬性等,下面我們來看看不同類型的Node節點其nodeName、nodeValue、attributes三個屬性分別代表的是什麼:

Interface nodeName nodeValue attributes
Attr same as Attr.name same as Attr.value null
CDATASection "#cdata-section" same as CharacterData.data, the content of the CDATA Section null
Comment "#comment" same as CharacterData.data, the content of the comment null
Document "#document" null null
DocumentFragment "#document-fragment" null null
DocumentType same as DocumentType.name null null
Element same as Element.tagName null NamedNodeMap
Entity entity name null null
EntityReference name of entity referenced null null
Notation notation name null null
ProcessingInstruction same as ProcessingInstruction.target same as ProcessingInstruction.data null
Text "#text" same as CharacterData.data, the content of the text node null

其實我們用的最多的就是Element和Text,通過Element的nodeName屬性可以得到這個節點的標籤名,Text對象的nodeValue得到的就是元素節點的文本值內容,下面我們來看看一個通過DOM解析xml的一個代碼案例:

首先我們構建一個xml的文檔,這個文檔等下會放在我們的伺服器上,通過http協議來得到這個xml文檔,然後在我們的Android用戶端對其進行解析

<?xml version="1.0" encoding="UTF-8"?><persons>    <person id="1">        <name>小羅</name>        <age>21</age>    </person>    <person id="2">        <name>android</name>        <age>15</age>    </person></persons>

下面我們來看看DOM解析伺服器端xml的工具類:

public class DomParserUtils{    public static List<Person> parserXmlByDom(InputStream inputStream) throws Exception    {        List<Person> persons = new ArrayList<Person>();        //    得到一個DocumentBuilderFactory解析工廠類        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();        //    得到一個DocumentBuilder解析類        DocumentBuilder builder = factory.newDocumentBuilder();        //    接收一個xml的字串來解析xml,Document代表整個xml文檔        Document document = builder.parse(inputStream);        //    得到xml文檔的根項目節點        Element personsElement = document.getDocumentElement();        //    得到標籤為person的Node對象的集合NodeList        NodeList nodeList = personsElement.getElementsByTagName("person");        for(int i = 0; i < nodeList.getLength(); i++)        {            Person person = new Person();            //    如果該Node是一個Element            if(nodeList.item(i).getNodeType() == Document.ELEMENT_NODE)            {                Element personElement = (Element)nodeList.item(i);                //    得到id的屬性值                String id = personElement.getAttribute("id");                person.setId(Integer.parseInt(id));                                //    得到person元素下的子項目                NodeList childNodesList = personElement.getChildNodes();                for(int j = 0; j < childNodesList.getLength(); j++)                {                    if(childNodesList.item(j).getNodeType() == Document.ELEMENT_NODE)                    {                        //    解析到了person下面的name標籤                        if("name".equals(childNodesList.item(j).getNodeName()))                        {                            //    得到name標籤的文本值                            String name = childNodesList.item(j).getFirstChild().getNodeValue();                            person.setName(name);                        }                        else if("address".equals(childNodesList.item(j).getNodeName()))                        {                            String age = childNodesList.item(j).getFirstChild().getNodeValue();                            person.setAge(Integer.parseInt(age));                        }                    }                }                                persons.add(person);                person = null;            }        }        return persons;    }}

通過DOM解析xml的好處就是,我們可以隨時訪問到某個節點的相鄰節點,並且對xml文檔的插入也非常的方便,不好的地方就是,其會將整個xml文檔載入到記憶體中,這樣會大大的佔用我們的記憶體資源,對於手機來說,記憶體資源是非常非常寶貴的,所以在手機當中,通過DOM這種方式來解析xml是用的比較少的。

二、SAX解析XML

SAX(Simple API for XML),接著我們來看看另一種解析xml的方式,通過sax來對xml文檔進行解析。

SAX是一個解析速度快並且佔用記憶體少的xml解析器,非常適合用於Android等行動裝置。 SAX解析XML檔案採用的是事件驅動,也就是說,它並不需要解析完整個文檔,在按內容順序解析文檔的過程中,SAX會判斷當前讀到的字元是否合法XML文法中的某部分,如果符合就會觸發事件。所謂事件,其實就是一些回調(callback)方法,這些方法(事件)定義在ContentHandler介面。下面是一些ContentHandler介面常用的方法:

startDocument()當遇到文檔的開頭的時候,調用這個方法,可以在其中做一些預先處理的工作。endDocument()和上面的方法相對應,當文檔結束的時候,調用這個方法,可以在其中做一些善後的工作。startElement(String namespaceURI, String localName, String qName, Attributes atts)當讀到一個開始標籤的時候,會觸發這個方法。namespaceURI就是命名空間,localName是不帶命名空間首碼的標籤名,qName是帶命名空間首碼的標籤名。通過atts可以得到所有的屬性名稱和相應的值。要注意的是SAX中一個重要的特點就是它的串流,當遇到一個標籤的時候,它並不會紀錄下以前所碰到的標籤,也就是說,在startElement()方法中,所有你所知道的資訊,就是標籤的名字和屬性,至於標籤的嵌套結構,上層標籤的名字,是否有子元屬等等其它與結構相關的資訊,都是不得而知的,都需要你的程式來完成。這使得SAX在編程處理上沒有DOM來得那麼方便。endElement(String uri, String localName, String name)這個方法和上面的方法相對應,在遇到結束標籤的時候,調用這個方法。characters(char[] ch, int start, int length)這個方法用來處理在XML檔案中讀到的內容,第一個參數用於存放檔案的內容,後面兩個參數是讀到的字串在這個數組中的起始位置和長度,使用new String(ch,start,length)就可以擷取內容。

上面提到了重要的一點,sax解析xml是基於事件流的處理方式的,因此每解析到一個標籤,它並不會記錄這個標籤之前的資訊,而我們只會知道當前這個表情的名字和它的屬性,至於標籤裡面的嵌套,上層標籤的名字這些都是無法知道的。

sax解析xml最重要的步驟就是定義一個我們自己的Handler處理類,我們可以讓其繼承 DefaultHandler 這個類,然後在裡面重寫其回調方法,在這些回調方法裡來做我們的xml解析

下面我們就通過一個執行個體來看看如果通過SAX來解析xml,首先定義一個我們自己的Handler類:

public class MyHandler extends DefaultHandler{    private List<Person> persons;    private Person person;    //    存放當前解析到的標籤名字    private String currentTag;    //    存放當前解析到的標籤的文本值    private String currentValue;        public List<Person> getPersons()    {        return persons;    }        //    當解析到文檔開始時的回調方法    @Override    public void startDocument() throws SAXException    {        persons = new ArrayList<Person>();    }        //    當解析到xml的標籤時的回調方法    @Override    public void startElement(String uri, String localName, String qName,            Attributes attributes) throws SAXException    {        if("person".equals(qName))        {            person = new Person();            //    得到當前元素的屬性值            for(int i = 0; i < attributes.getLength(); i++)            {                if("id".equals(attributes.getQName(i)))                {                    person.setId(Integer.parseInt(attributes.getValue(i)));                }            }        }        //    設定當前的標籤名        currentTag = qName;    }        //    當解析到xml的常值內容時的回調方法    @Override    public void characters(char[] ch, int start, int length)            throws SAXException    {        //    得到當前的常值內容        currentValue = new String(ch,start, length);        //    當currentValue不為null、""以及換行時        if(currentValue != null && !"".equals(currentValue) && !"\n".equals(currentValue))        {            //    判斷當前的currentTag是哪個標籤            if("name".equals(currentTag))            {                person.setName(currentValue);            }            else if("age".equals(currentTag))            {                person.setAge(Integer.parseInt(currentValue));            }        }        //    清空currentTag和currentValue        currentTag = null;        currentValue = null;    }        //    當解析到標籤的結束時的回調方法    @Override    public void endElement(String uri, String localName, String qName)            throws SAXException    {        if("person".equals(qName))        {            persons.add(person);            person = null;        }    }}

接著看看SAX解析xml的Util類:

public class SaxParserUtils{    public static List<Person> parserXmlBySax(InputStream inputStream) throws Exception    {        //    建立一個SAXParserFactory解析工廠類        SAXParserFactory factory = SAXParserFactory.newInstance();        //    執行個體化一個SAXParser解析類        SAXParser parser = factory.newSAXParser();        //    執行個體化我們的MyHandler類        MyHandler myHandler = new MyHandler();        //    根據我們自訂的Handler來解析xml文檔        parser.parse(inputStream, myHandler);        return myHandler.getPersons();    }}

三、PULL解析XML

最後來介紹第三種解析xml的方式,pull。pull解析和sax解析類似,都是基於事件流的方式,在Android中內建了pull解析的jar包,所以我們不需要匯入第三方的jar包了。

Pull解析器和SAX解析器的區別:

Pull解析器和SAX解析器雖有區別但也有相似性。他們的區別為:SAX解析器的工作方式是自動將事件推入註冊的事件處理器進行處理,因此你不能控制事件的處理主動結束;

而Pull解析器的工作方式為允許你的應用程式代碼主動從解析器中擷取事件,正因為是主動擷取事件,因此可以在滿足了需要的條件後不再擷取事件,結束解析。這是他們主要的區別。

而他們的相似性在運行方式上,Pull解析器也提供了類似SAX的事件(開始文檔START_DOCUMENT和結束文檔END_DOCUMENT,開始元素START_TAG和結束元素END_TAG,遇到元素內容TEXT等),但需要調用next() 方法提取它們(主動提取事件)。

Android系統中和Pull方式相關的包為org.xmlpull.v1,在這個包中提供了Pull解析器的工廠類XmlPullParserFactory和Pull解析器XmlPullParser,XmlPullParserFactory執行個體調用newPullParser方法建立XmlPullParser解析器執行個體,接著XmlPullParser執行個體就可以調用getEventType()和next()等方法依次主動提取事件,並根據提取的事件類型進行相應的邏輯處理。

下面我們就來通過一個代碼來看看pull解析xml的步驟:

public class PullParserUtils{    public static List<Person> parserXmlByPull(InputStream inputStream) throws Exception    {        List<Person> persons = null;        Person person = null;                //    建立XmlPullParserFactory解析工廠        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();        //    通過XmlPullParserFactory工廠類執行個體化一個XmlPullParser解析類        XmlPullParser parser = factory.newPullParser();        //    根據指定的編碼來解析xml文檔        parser.setInput(inputStream, "utf-8");                //    得到當前的事件類型        int eventType = parser.getEventType();        //    只要沒有解析到xml的文檔結束,就一直解析        while(eventType != XmlPullParser.END_DOCUMENT)        {            switch (eventType)            {                //    解析到文檔開始的時候                case XmlPullParser.START_DOCUMENT:                     persons = new ArrayList<Person>();                break;                //    解析到xml標籤的時候                case XmlPullParser.START_TAG:                     if("person".equals(parser.getName()))                     {                         person = new Person();                         //    得到person元素的第一個屬性,也就是ID                         person.setId(Integer.parseInt(parser.getAttributeValue(0)));                     }                     else if("name".equals(parser.getName()))                     {                         //    如果是name元素,則通過nextText()方法得到元素的值                         person.setName(parser.nextText());                     }                     else if("age".equals(parser.getName()))                     {                         person.setAge(Integer.parseInt(parser.nextText()));                     }                break;                //    解析到xml標籤結束的時候                case XmlPullParser.END_TAG:                     if("person".equals(parser.getName()))                     {                         persons.add(person);                         person = null;                     }                break;            }            //    通過next()方法觸發下一個事件            eventType = parser.next();        }                return persons;    }}

最後我們再編寫一個HttpUtils類來訪問我們的伺服器端的xml文檔:

public class HttpUtils{    public static InputStream httpMethod(String path, String encode)    {        HttpClient httpClient = new DefaultHttpClient();                try        {            HttpPost httpPost = new HttpPost(path);            HttpResponse httpResponse = httpClient.execute(httpPost);            if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)            {                HttpEntity httpEntity = httpResponse.getEntity();                return httpEntity.getContent();            }        }        catch (Exception e)        {            e.printStackTrace();        }        finally        {            httpClient.getConnectionManager().shutdown();        }                return null;    }}

最後來看看我們的Android應用程式的布局檔案以及Activity類的代碼:

public class MainActivity extends Activity{    private Button button;    private Button button2;    private Button button3;    private final String PATH = "http://172.25.152.34:8080/httptest/person.xml";    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                button = (Button)findViewById(R.id.button1);        button2 = (Button)findViewById(R.id.button2);        button3 = (Button)findViewById(R.id.button3);                ButtonOnClickListener listener = new ButtonOnClickListener();        button.setOnClickListener(listener);        button2.setOnClickListener(listener);        button3.setOnClickListener(listener);    }        class ButtonOnClickListener implements OnClickListener    {        @Override        public void onClick(View v)        {            Button button = (Button)v;            switch (button.getId())            {                case R.id.button1:                    //    啟動一個新線程解析xml                    class MyThread1 extends Thread                    {                        @Override                        public void run()                        {                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");                            List<Person> persons = null;                            try                            {                                persons = DomParserUtils.parserXmlByDom(inputStream);                            }                            catch (Exception e)                            {                                e.printStackTrace();                            }                            System.out.println("dom --->>" + persons);                        }                    }                    new MyThread1().start();                break;                case R.id.button2:                    //    啟動一個新線程解析xml                    class MyThread2 extends Thread                    {                        @Override                        public void run()                        {                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");                            List<Person> persons = null;                            try                            {                                persons = SaxParserUtils.parserXmlBySax(inputStream);                            }                            catch (Exception e)                            {                                e.printStackTrace();                            }                            System.out.println("sax --->>" + persons);                        }                    }                    new MyThread2().start();                break;                case R.id.button3:                    //    啟動一個新線程解析xml                    class MyThread3 extends Thread                    {                        @Override                        public void run()                        {                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");                            List<Person> persons = null;                            try                            {                                persons = PullParserUtils.parserXmlByPull(inputStream);                            }                            catch (Exception e)                            {                                e.printStackTrace();                            }                            System.out.println("pull: --->>" + persons);                        }                    }                    new MyThread3().start();                break;            }        }    }    @Override    public boolean onCreateOptionsMenu(Menu menu)    {        getMenuInflater().inflate(R.menu.main, menu);        return true;    }}

最後我們來看看控制台的輸出:

 

 

總結:dom方式解析xml,比較簡單,並可以訪問兄弟元素,但是需要將整個xml文檔載入到記憶體中,對於android裝置來說,不推薦使用dom的方式解析xml。

sax和pull都是基於事件驅動的xml解析器,在解析xml時並不會載入整個的xml文檔,佔用記憶體較少,因此在android開發中建議使用sax或者pull來解析xml文檔。

Android系列--DOM、SAX、Pull解析XML

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.