深入理解Android解析XML(一)

                                           Android解析XML方法詳解(一)

一、Android解析xml有3種方法,SAX  ,DOM ,  PULL

                        1、Simple API for XML(SAX) 

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

                編寫步驟:

                    ①創建一個SAX解析器工廠對象SAXParserFactory spf = SAXParseFactory.newInstance(); 

                    ②創建一個SAX解析器SAXParser saxParser = spf.newInstance();

                    ③設置解析器的相關特性,true表示開啓命名空間特性saxParser.setProperty("http://xml.org/sax/features/namespaces",true);

                    ④創建一個XMLContentHandler handler = new XMLContentHandler();

                            這個類 XMLContentHandler extends DefaultHandler 然後覆寫其中的回調方法

                                  1)覆寫startDocument(),這個是文檔的開始觸發,一般創建一個list集合存放所有對象

                                  2)覆寫startElement(String namespaceURI,String localName,String aName,Attributes atts),這個是開始標籤觸發,

                                         localName 是本地名,atts是屬性結合

                                         例如:裏面寫這些

                                    if(localName.equals("person")){

                                                  currentPerson = new Person();

                                                  currentPerson.setId(Integer.parseInt(atts.getValue("id")));

                                     }

                                  3)覆寫endElement(String uri,String localName,String name),結束標籤會觸發

                                                   例如寫這些

                                                     if(localName.equals("person")){

                                                              persons.add(currentPerson)

                                                              currentPerson = null;

                                                      }               

                   ⑤ saxParser.parse(inStream,handler);

                   ⑥inStream.close();

                   ⑦return handler.getPersons()

  
   
   

               2、DOM解析XML文件

                    DOM解析XML文件時,會將XML文件的所有內容讀取到內存中,然後允許您使用DOM API遍XML樹、檢索所需的數據。使用DOM操作XML的代碼看起來比較直觀,並且,在某些方面比基於SAX的實現更加簡單。但是,因爲DOM需要將XML文件的所有內容讀取到內存中,所以內存的消耗比較大,特別對於運行Android的移動設備來說,因爲設備的資源比較寶貴,所以建議還是採用SAX來解析XML文件,當然,如果XML文件的內容比較小採用DOM是可行的。

              編寫步驟:

               ①創建DOM工廠對象DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

               ②創建DocumentBuilder builder = factory.newDocumentBuilder();

               ③將流解析成 DOM對象 Document dom = builder.parse(inStream);

               ④獲取跟元素Element root = dom.getDocumentElement();

               ⑥獲取全部子節點NodeList items = root.getElementsByTagName("person");

               ⑦for(int i =0;i<items.getLength();i++){

                      Person person = new Person()

                      Element personNode =(Element)items.item(i);

      person.setId(new Integer(personNode.getAttribute("id")));

                      NodeList childsNodes = personNode.getChildNodes();

                      for (int j=0;j<childsNodes.getLength();j++){

Node node = (Node)childsNodes.item(j)

                                        if(node.getNodeType() ==Node.ELEMENT_NODE){ //判斷是否爲元素類型

                                                    Element childNode =(Element)node;

                                                    if("name".equals(childNode.getNodeName())){

                                                              //獲取name元素下的Text節點,然後從Text節點獲取數據

                                                              person.setName(childNode.getFirstChild().getNodeValue())

                    }

                                         }

                        }

                        persons.add(person)

                        inStream.close()

    

               3、Pull解析器解析XML文件

                     Pull解析器的運行方式與 SAX 解析器相似。它提供了類似的事件,如:開始元素和結束元素事件,使用parser.next()可以進入下一個元素並觸發相應事件。事件將作爲數值代碼被髮送,因此可以使用一個switch對感興趣的事件進行處理。當元素開始解析時,調用parser.nextText()方法可以獲取下一個Text類型元素的值。

               編寫步驟:

                   ①創建pull解析器 XmlPullParser parser = Xml.newPullParser();

                   ②獲取當前事件類型 int eventType = parser.getEventType();

                   ③while(eventType!=XmlPullParser.END_DOCUMENT){

                                         switch(eventType){

                                               case XmlPullParser.START_DOCUMENT://文檔開始事件,可以進行數據初始化處理

                                                                  persons = new ArrayList<Person>();break;

                                               case XmlPullParser.START_TAG://開始元素事件

                                                                 String name = parser.getName();

                                                                 if(name.equalsIgnoreCase("person")){

                                                                            currentPerson = new Person();

                                                                            currentPerson.setId(new Integer(parser.getAttributeValue(null,"id")));

                                                                 }else if(currentPerson!=null){

                                                                                 if(name.equalsIgnoreCase("name")){

         currentPerson.setName(parser.nextText());//如果後面是Text元素,即返回他的值

   }else if (name.equalsIgnoreCase("age")){

currentPerson.setAge(new Short(parser.nextText()));

}

        }

  break;

                                              case XmlPullParser.END_TAG://結束元素事件

                                                         if(parser.getName().equalsIgnoreCase("person")&&currentPerson!=null){

                                                                       persons.add(currentPerson);

                                                                       currentPerson = null;

                                                         }

                                                         break;

                                                 }

                                                 eventType = parser.next();//解析器始終下移

                                       }


              4、SAX和PULL使用

                  區別爲:SAX解析器的工作方式是自動將事件推入事件處理器進行處理,因此你不能控制事件的處理主動結束;而Pull解析器的工作方式爲允許你的應用程序代碼主動從解析器中獲取事件,正因爲是主動獲取事件,因此可以在滿足了需要的條件後不再獲取事件,結束解析。
                  你隨便找個sax和pull的例子比較一下就可以發現,pull是一個while循環,隨時可以跳出,而sax不是,sax是隻要解析了,就必須解析完成。

              5、總結:

                      對於這三種解析器各有優點,我個人比較傾向於PULL解析器,因爲SAX解析器操作起來太笨重,DOM不適合文檔較大,內存較小的場景,唯有PULL輕巧靈活,速度快,佔用內存小,使用非常順手。讀者也可以根據自己的喜好選擇相應的解析技術。

             

         下面是3種方式的完整例子

            SAX例子

               定義一個xml,和JavaBean

<?xml version="1.0" encoding="UTF-8"?>
<persons>
  <person id="23">
    <name>Bob</name>
    <age>23</age>
  </person>
  <person id="21">
    <name>劉德華</name>
    <age>48</age>
  </person>
</persons>
public class Person {

    private Integer id;
    private String name;
private Short age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
}

    public Short getAge() {
        return age;
    }

    public void setAge(Short age) {
        this.age = age;
    }
}

              使用SAX解析XML例子

 

public static List<Person> readXML(InputStream inStream) {
   try {
            //創建解析器
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser saxParser = spf.newSAXParser();
 
            //設置解析器的相關特性,true表示開啓命名空間特性
            saxParser.setProperty("http://xml.org/sax/features/namespaces",true);
            XMLContentHandler handler = new XMLContentHandler();
            saxParser.parse(inStream, handler);
            inStream.close();

            return handler.getPersons();
   } catch (Exception e) {
            e.printStackTrace();
   }

  return null;
}

 
//SAX類:DefaultHandler,它實現了ContentHandler接口。在實現的時候,只需要繼承該類,重載相應的方法即可。
public class XMLContentHandler extends DefaultHandler {

            private List<Person> persons = null;
            private Person currentPerson;
            private String tagName = null;//當前解析的元素標籤

             public List<Person> getPersons() {
                        return persons;
            }
 
            //接收文檔開始的通知。當遇到文檔的開頭的時候,調用這個方法,可以在其中做一些預處理的工作。
            @Override
            public void startDocument() throws SAXException {
                        persons = new ArrayList<Person>();
            }

             //接收元素開始的通知。當讀到一個開始標籤的時候,會觸發這個方法。其中namespaceURI表示元素的命名空間;
            //localName表示元素的本地名稱(不帶前綴);qName表示元素的限定名(帶前綴);atts 表示元素的屬性集合
            @Override
            public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {

                        if(localName.equals("person")){
                                    currentPerson = new Person();
                                    currentPerson.setId(Integer.parseInt(atts.getValue("id")));
                        }

                        this.tagName = localName;
            }
 
            //接收字符數據的通知。該方法用來處理在XML文件中讀到的內容,第一個參數用於存放文件的內容,
            //後面兩個參數是讀到的字符串在這個數組中的起始位置和長度,使用new String(ch,start,length)就可以獲取內容。
            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {

                        if(tagName!=null){
                                    String data = new String(ch, start, length);
                                    if(tagName.equals("name")){
                                                this.currentPerson.setName(data);
                                    }else if(tagName.equals("age")){
                                                this.currentPerson.setAge(Short.parseShort(data));
                                    }
                        }
            }

             //接收文檔的結尾的通知。在遇到結束標籤的時候,調用這個方法。其中,uri表示元素的命名空間;
            //localName表示元素的本地名稱(不帶前綴);name表示元素的限定名(帶前綴)
            @Override
            public void endElement(String uri, String localName, String name) throws SAXException {

                        if(localName.equals("person")){
                                    persons.add(currentPerson);
                                    currentPerson = null;
                        }

                        this.tagName = null;
            }
}
                 使用DOM解析xml例子

public static List<Person> readXML(InputStream inStream) {

            List<Person> persons = new ArrayList<Person>();

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            try {
                        DocumentBuilder builder = factory.newDocumentBuilder();
                        Document dom = builder.parse(inStream);

                        Element root = dom.getDocumentElement();

                        NodeList items = root.getElementsByTagName("person");//查找所有person節點

                        for (int i = 0; i < items.getLength(); i++) {
                                    Person person = new Person();

                                    //得到第一個person節點
                                    Element personNode = (Element) items.item(i);

                                    //獲取person節點的id屬性值
                                    person.setId(new Integer(personNode.getAttribute("id")));

                                    //獲取person節點下的所有子節點(標籤之間的空白節點和name/age元素)
                                    NodeList childsNodes = personNode.getChildNodes();

                                    for (int j = 0; j < childsNodes.getLength(); j++) {
                                                Node node = (Node) childsNodes.item(j); //判斷是否爲元素類型

                                                if(node.getNodeType() == Node.ELEMENT_NODE){   
                                                            Element childNode = (Element) node;

                                                        //判斷是否name元素
                                                            if ("name".equals(childNode.getNodeName())) {
                                                                        //獲取name元素下Text節點,然後從Text節點獲取數據
                                                                       person.setName(childNode.getFirstChild().getNodeValue());                                                                               } else if (“age”.equals(childNode.getNodeName())) {
                                                                        person.setAge(new Short(childNode.getFirstChild().getNodeValue()));
                                                            }
                                                }
                                    }

                                    persons.add(person);
                        }

                        inStream.close();
            } catch (Exception e) {
                        e.printStackTrace();
            }

            return persons;
}



               使用pull解析xml例子

 

//讀取XML
public static List<Person> readXML(InputStream inStream) {

            XmlPullParser parser = Xml.newPullParser();

            try {
                        parser.setInput(inStream, "UTF-8");
                        int eventType = parser.getEventType();

                        Person currentPerson = null;
                        List<Person> persons = null;

                        while (eventType != XmlPullParser.END_DOCUMENT) {
                                    switch (eventType) {
                                    case XmlPullParser.START_DOCUMENT://文檔開始事件,可以進行數據初始化處理
                                                persons = new ArrayList<Person>();
                                                break;

                                     case XmlPullParser.START_TAG://開始元素事件
                                                String name = parser.getName();
                                                if (name.equalsIgnoreCase("person")) {
                                                            currentPerson = new Person();
                                                            currentPerson.setId(new Integer(parser.getAttributeValue(null, "id")));
                                                } else if (currentPerson != null) {
                                                            if (name.equalsIgnoreCase("name")) {
                                                                        currentPerson.setName(parser.nextText());// 如果後面是Text元素,即返回它的值
                                                            } else if (name.equalsIgnoreCase("age")) {
                                                                        currentPerson.setAge(new Short(parser.nextText()));
                                                            }
                                                }
                                                break;
 
                                    case XmlPullParser.END_TAG://結束元素事件
                                                if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) {
                                                            persons.add(currentPerson);
                                                            currentPerson = null;
                                                }

                                                break;
                                    }

                                     eventType = parser.next();
                        }

            inStream.close();
            return persons;
            } catch (Exception e) {
                        e.printStackTrace();
            }

            return null;
}

 
//成XML文件
//使用Pull解析器生成一個與itcast.xml文件內容相同的myitcast.xml文件。
public static String writeXML(List<Person> persons, Writer writer){

    XmlSerializer serializer = Xml.newSerializer();

    try {
        serializer.setOutput(writer);
        serializer.startDocument("UTF-8", true);

        //第一個參數爲命名空間,如果不使用命名空間,可以設置爲null
        serializer.startTag("", "persons");

        for (Person person : persons){
            serializer.startTag("", "person");
            serializer.attribute("", "id", person.getId().toString());
            serializer.startTag("", "name");
            serializer.text(person.getName());
            serializer.endTag("", "name");
            serializer.startTag("", "age");
            serializer.text(person.getAge().toString());
            serializer.endTag("", "age");
            serializer.endTag("", "person");
        }

        serializer.endTag("", "persons");
        serializer.endDocument();

        return writer.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

 
//使用代碼如下(生成XML文件):

File xmlFile = new File("myitcast.xml");
FileOutputStream outStream = new FileOutputStream(xmlFile);
OutputStreamWriter outStreamWriter = new OutputStreamWriter(outStream, "UTF-8");
BufferedWriter writer = new BufferedWriter(outStreamWriter);

writeXML(persons, writer);
writer.flush();
writer.close();

 
//如果只想得到生成的xml內容,可以使用StringWriter:
StringWriter writer = new StringWriter();
writeXML(persons, writer);
String content = writer.toString();



總結:

對於這三種解析器各有優點,我個人比較傾向於PULL解析器,因爲SAX解析器操作起來太笨重,DOM不適合文檔較大,內存較小的場景,唯有PULL輕巧靈活,速度快,佔用內存小,使用非常順手。讀者也可以根據自己的喜好選擇相應的解析技術。

發佈了34 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章