XML是一種通用的資料交換格式,它的平台無關性、語言無關性、系統無關性、給資料內建與互動帶來了極大的友善。XML在不同的語言環境中解析方式都是一樣的,隻不過實作的文法不同而已。
對于XML的解析,還有一些友善的XML解析工具,如JAXB、XStream這樣的解析工具,它們利用注解、反射等非常友善地幫助我們實作從XML解析為Java對象,還有如Dom4j,它是一個易用的、開源的庫,特别是對進行讀取和修改XML檔案内容提供了豐富的API接口,易操作、高效率,但是它對于XML内容和JAVA對象之間的映射轉換卻不太如JAXB和XStream。
一、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檔案比較大,容易影響解析性能且可能會造成記憶體溢出。
DOM解析示例:
項目目錄結構
1.DOM解析
TestXML.java檔案
import java.io.FileReader;
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.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
//采用DOM進行解析
//DOM的讀取時是将XML整個檔案裝載成DOM樹,即使文檔中有不需要的資訊時也會被裝載進來,在大多數的時候DOM是非常友善好用的。
public class TestXMl {
public static void main(String[] args) {
// 文檔建構器的工廠
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 得到XML文檔建構器
try {
DocumentBuilder db = dbf.newDocumentBuilder();
// 1.得到XML文檔對象
// 當test.xml放在本項目的根目錄時(和src bin同一目錄下時)
// db.parse("test.xml");
// 當test.xml放在bin目錄下時
String path = FileReader.class.getResource("/").getFile();
/// System.out.println(path);
// 導入的是org.w3c.dom.Document;
Document doc = db.parse(path + "test.xml");
// 得到文檔根節點
Element root = doc.getDocumentElement();
String rootName = root.getNodeName();
System.out.println(rootName);
NodeList list = doc.getElementsByTagName("People");
// 2.擷取XML中的節點
System.out.println("目前有幾個People:" + list.getLength());
// 3.得到節點裡面的有用資訊
for (int i = 0; i < list.getLength(); i++) {
Element peopleElement = (Element) list.item(i);
String id = peopleElement.getAttribute("id"); // 擷取id的屬性值
System.out.println("id:" + id);
// 周遊目前元素的子節點
NodeList childNodeList = peopleElement.getChildNodes();
// test.xml中一個People元素下有Name和Age兩個标簽,但是它把回車也算作一個節點,是以列印出來是五個
// 第一個是沒有回車的,第二個有
System.out.println("子節點個數:" + childNodeList.getLength());
// 判斷這些子節點中的元素是不是有效的元素,即不為回車
for (int j = 0; j < childNodeList.getLength(); j++) {
// 判斷是不是元素,有标簽的,若是元素才把它轉換為元素
if (childNodeList.item(j) instanceof Element) {
Element cElement = (Element) childNodeList.item(j);
String tagName = cElement.getTagName();
String content = cElement.getTextContent();
System.out.println(tagName + ":" + content);
}
}
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.往XML檔案中寫入的是Document對象
Write.java檔案
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
//由對象到檔案,把document對象儲存到檔案中
//XML是磁盤檔案,DOM是記憶體對象。
//往XML檔案中寫入的是Document對象,是以要編輯XML檔案,實際上編輯的是Document文檔對象。
public class WriteXML {
public static void main(String[] args) {
//建立一個document,通過builder建立
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
//通過document建立節點Element
Element stuEle = doc.createElement("Student");
stuEle.setAttribute("stuNo","S1001");
//給stuEle添加子節點
Element nameEle = doc.createElement("Name");
//方式一:建立一個文本節點
/*Text txtName = doc.createTextNode("zhangsan");
nameEle.appendChild(txtName);*/
//方式二:直接添加文本
nameEle.setTextContent("zhangsan");
Element ageEle = doc.createElement("Age");
ageEle.setTextContent("20");
stuEle.appendChild(nameEle);
stuEle.appendChild(ageEle);
//将Element對象添加到document中
doc.appendChild(stuEle);
//将document對象儲存到硬碟檔案xml中
//通過transformer進行儲存
TransformerFactory tf = TransformerFactory.newInstance();
Transformer former = tf.newTransformer();
former.transform(new DOMSource(doc), new StreamResult("stu.xml"));
System.out.println("生成xml成功...");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<PeopleList>
<People id="1">
<Name en="zhangsan">張三</Name>
<Age>20</Age>
<Address>蘇州</Address>
</People>
<People id="2">
<Name en="lisi">李四</Name>
<Age>39</Age>
<Address>常州</Address>
</People>
<People id="3">
<Name en="wangwu">王五</Name>
<Age>14</Age>
<Address>楊州</Address>
</People>
<People id="4">
<Name en="zhaowu">趙武</Name>
<Age>31</Age>
<Address>貴州</Address>
</People>
<People id="5">
<Name en="sunliu">孫劉</Name>
<Age>26</Age>
<Address>廣州</Address>
</People>
</PeopleList>
二、SAX解析
SAX的全稱是Simple APIs for XML,也即XML簡單應用程式接口。與DOM不同,SAX提供的通路模式是一種順序模式,這是一種快速讀寫XML資料的方式。當使用SAX分析器對XML文檔進行分析時,會觸發一系列事件,并激活相應的事件處理函數,應用程式通過這些事件處理函數實作對XML文檔的通路,因而SAX接口也被稱作事件驅動接口。
優點:
1、采用事件驅動模式,對記憶體耗費比較小。
2、适用于隻處理XML檔案中的資料時。
缺點:
1、編碼比較麻煩。
2、很難同時通路XML檔案中的多處不同資料。
SAX解析示例
XMLHandler.java
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class XMLHandler extends DefaultHandler {
private String value; //讀取到的文本
private People people; //臨時存放的一個人
private List<People> peopleList;
public List<People> getPeopleList() {
return peopleList;
}
//當讀到文檔開始處觸發事件,該方法隻執行一次
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
System.out.println("開始執行startDocument,開始讀取xml文檔");
peopleList = new ArrayList<People>();
}
//當讀到文檔結束處觸發事件,該方法隻執行一次
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
System.out.println("開始執行endDocument,結束讀取xml文檔");
}
//當讀到元素開始處觸發事件
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
super.startElement(uri, localName, qName, attributes);
System.out.println("開始執行startElement,開始讀取xml文檔的标簽,該标簽為:"+qName);
if("People".equals(qName)) {
people = new People();
System.out.println("目前people有屬性"+attributes.getLength());
for(int i = 0;i<attributes.getLength();i++) {
//擷取目前屬性名
if("id".equals(attributes.getQName(i))) {
people.setId(attributes.getValue(i));
}
}
}
}
//當讀到元素結束處觸發事件
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// TODO Auto-generated method stub
super.endElement(uri, localName, qName);
System.out.println("開始執行endElement,結束讀取xml文檔的标簽,該标簽為:"+qName);
if("People".equals(qName)) {
peopleList.add(people);
people = null;
}else if("Name".equals(qName)) { //讀到Name的結束标簽
people.setName(value);
}else if("Age".equals(qName)) {
people.setAge(value);
}
}
//讀到内容時觸發事件
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// TODO Auto-generated method stub
super.characters(ch, start, length);
value = new String(ch,start,length);
System.out.println("目前讀到的文本是:"+value);
}
}
ReadXMLBySAX.java
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
//SAX解析
//流讀取的機制(SAX),Document的讀取在内部也是使用流的機制去讀的。
//流讀取的機制是隻負責讀取,不進行存儲,但是會給出一定的關鍵的事件,提示你進行相應的儲存操作。
public class ReadXMLBySAX {
public static void main(String[] args) {
//得到saxParser
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
XMLHandler handler = new XMLHandler();
//通過saxParse接卸器的parse方法,解析xml,parse需要一個處理器,handler
parser.parse("test.xml", handler);
List<People> list = handler.getPeopleList();
System.out.println("一共讀取倒了"+list.size()+"個人");
for(People people : list) {
System.out.println(people);
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
People.java
public class People {
private String id;
private String name;
private String age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public People(String id, String name, String age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public People() {
super();
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student[id="+id+",name="+name+",age="+age+"]";
}
}