XML 解析

XML 作爲數據存儲一種常見的格式,凡見代碼的地方,都可以見到,所以選擇何種解析方式也是比較重要的,合適的方式不僅可以提高編譯效率,還能節約內存,防止內存溢出!

下面就綜合各種博客(主要參考博客有:https://www.cnblogs.com/longqingyang/p/5577937.html),實測四種解析方式,代碼可運行,供給大家參考

XML的解析方式分爲四種:
1、DOM解析;
2、SAX解析;
3、JDOM解析;
4、DOM4J解析。
其中前兩種屬於基礎方法,是官方提供的平臺無關的解析方式;
後兩種屬於擴展方法,它們是在基礎的方法上擴展出來的,只適用於java平臺。

針對以下XML文件,會對四種方式進行詳細描述:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book id="001">
        <name>狂人日記</name>
        <year>1918</year>
        <price>14</price>
        <language>China</language>
    </book>
    <book id="002">
        <name>安徒生童話</name>
        <year>2004</year>
        <price>32</price>
        <language>English</language>
    </book>
</bookstore>

一、DOM解析
DOM的全稱是Document Object Model,也即文檔對象模型。在應用程序中,基於DOM的XML分析器將一個XML文檔轉換成一個對象模型的集合(通常稱DOM樹),應用程序正是通過對這個對象模型的操作,來實現對XML文檔數據的操作。通過DOM接口,應用程序可以在任何時候訪問XML文檔中的任何一部分數據,因此,這種利用DOM接口的機制也被稱作隨機訪問機制。
DOM接口提供了一種通過分層對象模型來訪問XML文檔信息的方式,這些分層對象模型依據XML的文檔結構形成了一棵節點樹。無論XML文檔中所描述的是什麼類型的信息,即便是製表數據、項目列表或一個文檔,利用DOM所生成的模型都是節點樹的形式。也就是說,DOM強制使用樹模型來訪問XML文檔中的信息。由於XML本質上就是一種分層結構,所以這種描述方法是相當有效的。
DOM樹所提供的隨機訪問方式給應用程序的開發帶來了很大的靈活性,它可以任意地控制整個XML文檔中的內容。然而,由於DOM分析器把整個XML文檔轉化成DOM樹放在了內存中,因此,當文檔比較大或者結構比較複雜時,對內存的需求就比較高。而且,對於結構複雜的樹的遍歷也是一項耗時的操作。所以,DOM分析器對機器性能的要求比較高,實現效率不十分理想。不過,由於DOM分析器所採用的樹結構的思想與XML文檔的結構相吻合,同時鑑於隨機訪問所帶來的方便,因此,DOM分析器還是有很廣泛的使用價值的。
優點:
1、形成了樹結構,有助於更好的理解、掌握,且代碼容易編寫。
2、解析過程中,樹結構保存在內存中,方便修改。
缺點:
1、由於文件是一次性讀取,所以對內存的耗費比較大。
2、如果XML文件比較大,容易影響解析性能且可能會造成內存溢出。
以下是解析代碼:
package com.study.xml;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class DomParse {
     public static void main(String[] args) throws SAXException {
          // 創建一個DocumentBuilderFactory的對象
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          // 創建一個DocumentBuilder的對象
          try {
              // 創建DocumentBuilder對象
              DocumentBuilder db = dbf.newDocumentBuilder();
              // 通過DocumentBuilder對象的parser方法加載books.xml文件到當前項目下
              Document document = db.parse("src/books.xml");
              // 獲取所有book節點的集合
              NodeList bookList = document.getElementsByTagName("book");
              // 遍歷每一個book節點
              for (int i = 0; i < bookList.getLength(); i++) {
                   // 通過 item(i)方法 獲取一個book節點,nodelist的索引值從0開始
                   Node book = bookList.item(i);
                   // 獲取book節點的所有屬性集合
                   NamedNodeMap attrs = book.getAttributes();
                   // 遍歷book的屬性
                   for (int j = 0; j < attrs.getLength(); j++) {
                        // 通過item(index)方法獲取book節點的某一個屬性
                        Node attr = attrs.item(j);
                        // 獲取屬性名
                        System.out.print("屬性:" + attr.getNodeName());
                        // 獲取屬性值
                        System.out.println("--" + attr.getNodeValue());
                   }
                   // 解析book節點的子節點
                   NodeList childNodes = book.getChildNodes();
                   // 遍歷childNodes獲取每個節點的節點名和節點值
                   for (int k = 0; k < childNodes.getLength(); k++) {
                        // 區分出text類型的node以及element類型的node
                        if (childNodes.item(k).getNodeType() == Node.ELEMENT_NODE) {
                             // 獲取了element類型節點的節點名
                             System.out.print(childNodes.item(k).getNodeName());
                             // 獲取了element類型節點的節點值
                             System.out.println("--" + childNodes.item(k).getFirstChild().getNodeValue());
                        }
                   }
              }
          } catch (ParserConfigurationException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          } catch (SAXException e) {
              e.printStackTrace();
          }
     }
}
二、SAX解析
SAX的全稱是Simple APIs for XML,也即XML簡單應用程序接口。與DOM不同,SAX提供的訪問模式是一種順序模式,這是一種快速讀寫XML數據的方式。當使用SAX分析器對XML文檔進行分析時,會觸發一系列事件,並激活相應的事件處理函數,應用程序通過這些事件處理函數實現對XML文檔的訪問,因而SAX接口也被稱作事件驅動接口。
優點:
1、採用事件驅動模式,對內存耗費比較小。
2、適用於只處理XML文件中的數據時。
缺點:
1、編碼比較麻煩。
2、很難同時訪問XML文件中的多處不同數據。
SAX解析可分四個步驟進行:
1、得到xml文件對應的資源,可以是xml的輸入流,文件和uri
2、得到SAX解析工廠(SAXParserFactory)
3、由解析工廠生產一個SAX解析器(SAXParser)
4、傳入輸入流和handler給解析器,調用parse()解析 
以下是解析代碼:
package com.study.xml;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
public class SaxParse {
     public static void main(String[] args) throws Exception {
          // 創建解析工廠
          SAXParserFactory factory = SAXParserFactory.newInstance();
          // 得到解析器
          SAXParser sp = factory.newSAXParser();
          // 得到解讀器
          XMLReader reader = sp.getXMLReader();
          // 設置內容處理器
          reader.setContentHandler(new JDomParse().new ListHandler());
          // 讀取xml的文檔內容
          reader.parse("src/books.xml");
     }
     class ListHandler implements ContentHandler {
          @Override
          public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
              // 解析到節點的開頭標籤'<..'時被調用,實際上就是節點開頭<xx>內容
              System.out.print("<" + qName);
              
              //獲取屬性
              for (int i = 0; atts != null && i < atts.getLength(); i++) {
                   System.out.print(" " + atts.getQName(i) + "='" + atts.getValue(i) + "'");
              }
              System.out.print(">");
          }
          @Override
          public void endElement(String uri, String localName, String qName) throws SAXException {
              // 解析到節點的開頭標籤'/>'時被調用,實際上就是節點結尾</xx>內容
              System.out.print("</" + qName + ">");
          }
         
          @Override
          public void characters(char[] ch, int start, int length) throws SAXException {
              //真正的節點內容,包括換行符和內容
              String content = new String(ch, start, length);
              System.out.print(content);
          }
          @Override
          public void setDocumentLocator(Locator locator) {
          }
          @Override
          public void startDocument() throws SAXException {
          }
          @Override
          public void endDocument() throws SAXException {
          }
          @Override
          public void startPrefixMapping(String prefix, String uri) throws SAXException {
          }
          @Override
          public void endPrefixMapping(String prefix) throws SAXException {
          }
          @Override
          public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
          }
          @Override
          public void processingInstruction(String target, String data) throws SAXException {
          }
          @Override
          public void skippedEntity(String name) throws SAXException {
          }
     }
}
三、JDOM解析

需要導入包:

jdom.jar

xercesImpl.jar (這個包不導入的話,會報 SAX2 driver class org.apache.xerces.parsers.SAXParser not found: SAX2 driver class org.apache.xerces.parsers.SAXParser not found 這個錯誤)

特徵:
1、僅使用具體類,而不使用接口。
2、API大量使用了Collections類。
以下是解析代碼:
package com.study.xml;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
public class JDomParse {
     public static void main(String[] args) throws JDOMException, IOException {
          SAXBuilder sb = new SAXBuilder();
          Document doc = sb.build(new File("src/books.xml"));
          Element root = doc.getRootElement();
          List list = root.getChildren("book");
          for (int i = 0; i < list.size(); i++) {
              Element element = (Element) list.get(i);
              String id = element.getAttributeValue("id");
              String name = element.getChildText("name");
              String year = element.getChildText("year");
              String price = element.getChildText("price");
              String language = element.getChildText("language");
              System.out.println("--" + id);
              System.out.println("--" + name);
              System.out.println("--" + year);
              System.out.println("--" + price);
              System.out.println("--" + language);
          }
     }
}
四、DOM4J解析

需要導入包:

dom4j-1.6.1.jar

特徵:
1、JDOM的一種智能分支,它合併了許多超出基本XML文檔表示的功能。
2、它使用接口和抽象基本類方法。
3、具有性能優異、靈活性好、功能強大和極端易用的特點。
4、是一個開放源碼的文件
以下是解析代碼:
package com.study.xml;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class Dom4JParse {
     public static void main(String[] args) {
          // 解析books.xml文件
          // 創建SAXReader的對象reader
          SAXReader reader = new SAXReader();
          try {
              // 通過reader對象的read方法加載books.xml文件,獲取docuemnt對象。
              Document document = reader.read(new File("src/books.xml"));
              // 通過document對象獲取根節點bookstore
              Element bookStore = document.getRootElement();
              // 通過element對象的elementIterator方法獲取迭代器
              Iterator it = bookStore.elementIterator();
              // 遍歷迭代器,獲取根節點中的信息(書籍)
              while (it.hasNext()) {
                   Element book = (Element) it.next();
                   // 獲取book的屬性名以及 屬性值
                   List<Attribute> bookAttrs = book.attributes();
                   for (Attribute attr : bookAttrs) {
                        System.out.println("屬性" + attr.getName() + "--" + attr.getValue());
                   }
                   Iterator itt = book.elementIterator();
                   while (itt.hasNext()) {
                        Element bookChild = (Element) itt.next();
                        System.out.println("節點" + bookChild.getName() + "--" + bookChild.getStringValue());
                   }
              }
          } catch (DocumentException e) {
              e.printStackTrace();
          }
     }
}
Final:比較總結
DOM4J性能最好,連Sun的JAXM也在用DOM4J。目前許多開源項目中大量採用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置文件。如果不考慮可移植性,那就採用DOM4J。

JDOM和DOM在性能測試時表現不佳,在測試10M文檔時內存溢出。在小文檔情況下還值得考慮使用DOM和JDOM。雖然JDOM的開發者已經說明他們期望在正式發行版前專注性能問題,但是從性能觀點來看,它確實沒有值得推薦之處。另外,DOM仍是一個非常好的選擇。DOM實現廣泛應用於多種編程語言。它還是許多其它與XML相關的標準的基礎,因爲它正式獲得W3C推薦(與基於非標準的Java模型相對),所以在某些類型的項目中可能也需要它(如在JavaScript中使用DOM)。
SAX表現較好,這要依賴於它特定的解析方式-事件驅動。一個SAX檢測即將到來的XML流,但並沒有載入到內存(當然當XML流被讀入時,會有部分文檔暫時隱藏在內存中)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章