dom4j的測試例子和原始碼詳解(重點對比和DOM、SAX的區別)
目錄
- 簡介
- DOM、SAX、JAXP和DOM4J
- xerces直譯器
- SAX
- DOM
- JAXP
- DOM解析器
- 獲取SAX解析器
- DOM4j
- 專案環境
- 工程環境
- 建立專案
- 引入依賴
- 使用例子--生成xml檔案
- 需求
- 生成xml檔案--使用w3c的DOM介面
- 主要步驟
- 編寫測試類
- 測試結果
- 生成xml檔案--使用dom4j的DOM介面
- 主要步驟
- 編寫測試類
- 測試結果
- 使用例子--解析xml檔案
- 需求
- 主要步驟
- 測試遍歷節點
- 測試XPath獲取指定節點
- XPath語法
- 原始碼分析
- dom4j節點的類結構
- SAXReader.read(File file)
- SAXReader.read(InputSource in)
- SAXContentHandler
- startDocument()
- startElement(String,String,String,Attributes)
- endElement(String, String, String)
- endDocument()
簡介
dom4j
用於建立和解析XML檔案,不是純粹的DOM
或SAX
,而是兩者的結合和改進,另外,dom4j
支援Xpath
來獲取節點。目前,由於其出色的效能和易用性,目前dom4j
已經得到廣泛使用,例如Spring
、Hibernate
就是使用dom4j
來解析xml配置。
注意,dom4j
使用Xpath
需要額外引入jaxen
的包。
DOM、SAX、JAXP和DOM4J
其實,JDK已經帶有可以解析xml的api,如DOM
、SAX
、JAXP
,但為什麼dom4j
會更受歡迎呢?它們有什麼區別呢?在學習dom4j
之前,需要先理解下DOM
、SAX
等概念,因為dom4j
就是在此基礎上改進而來。
xerces直譯器
先介紹下xerces
直譯器,下面介紹的SAX
、DOM
和JAXP
都只是介面,而xerces
直譯器就是它們的具體實現,在com.sun.org.apache.xerces.internal
包。xerces
被稱為效能最好的直譯器,除了xerces
外,還有其他的第三方直譯器,如crimson
。
SAX
JDK針對解析xml提供的介面,不是具體實現,在org.xml.sax
SAX
是基於事件處理,解析過程中根據當前的XML元素型別,呼叫使用者自己實現的回撥方法,如:startDocument()
;,startElement()
。下面以例子說明,通過SAX
解析xml並列印節點名:
/*這裡解釋下四個的介面:
EntityResolver:需要實現resolveEntity方法。當解析xml需要引入外部資料來源時觸發,通過這個方法可以重定向到本地資料來源或進行其他操作。
DTDHandler:需要實現notationDecl和unparsedEntityDecl方法。當解析到"NOTATION", "ENTITY"或 "ENTITIES"時觸發。
ContentHandler:最常用的一個介面,需要實現startDocument、endDocument、startElement、endElement等方法。當解析到指定元素型別時觸發。
ErrorHandler:需要實現warning、error或fatalError方法。當解析出現異常時會觸發。
*/
@Test
public void test04() throws Exception {
//DefaultHandler實現了EntityResolver, DTDHandler, ContentHandler, ErrorHandler四個介面
DefaultHandler handler = new DefaultHandler() {
@Override
//當解析到Element時,觸發列印該節點名
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println(qName);
}
};
//獲取解析器例項
XMLReader xr = XMLReaderFactory.createXMLReader();
//設定處理類
xr.setContentHandler(handler);
/*
* xr.setErrorHandler(handler);
* xr.setDTDHandler(handler);
* xr.setEntityResolver(handler);
*/
xr.parse(new InputSource("members.xml"));
}
因為SAX
是基於事件處理的,不需要等到整個xml檔案都解析完才執行我們的操作,所以效率較高。但SAX
存在一個較大缺點,就是不能隨機訪問節點,因為SAX
不會主動地去儲存處理過的元素(優點就是記憶體佔用小、效率高),如果想要儲存讀取的元素,開發人員先構建出一個xml樹形結構,再手動往裡面放入元素,非常麻煩(其實dom4j
就是通過SAX
來構建xml樹)。
DOM
JDK針對解析xml提供的介面,不是具體實現,在org.w3c.dom
包。DOM
採用瞭解析方式是一次性載入整個XML文件,在記憶體中形成一個樹形的資料結構,開發人員可以隨機地操作元素。見以下例子:
@SuppressWarnings("restriction")
@Test
public void test05() throws Exception {
//獲得DOMParser物件
com.sun.org.apache.xerces.internal.parsers.DOMParser domParser = new com.sun.org.apache.xerces.internal.parsers.DOMParser();
//解析檔案
domParser.parse(new InputSource("members.xml"));
//獲得Document物件
Document document=domParser.getDocument();
// 遍歷節點
printNodeList(document.getChildNodes());
}
通過DOM解析,我們可以獲取任意節點進行操作。但是,DOM
有兩個缺點:
- 由於一次性載入整個XML檔案到記憶體,當處理較大檔案時,容易出現記憶體溢位。
- 節點的操作還是比較繁瑣。
以上兩點,dom4j
都進行了相應優化。
JAXP
封裝了SAX
、DOM
兩種介面,它並沒有為JAVA解析XML提供任何新功能,只是對外提供更解耦、簡便操作的API。如下:
DOM解析器
@Test
public void test02() throws Exception {
// 獲得DocumentBuilder物件
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析xml檔案,獲得Document物件
Document document = builder.parse("members.xml");
// 遍歷節點
printNodeList(document.getChildNodes());
}
獲取SAX解析器
@Test
public void test03() throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
saxParser.parse("members.xml", new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println(qName);
}
});
}
其實,JAXP
並沒有很大程度提高DOM和SAX的易用性,更多地體現在獲取解析器時實現解耦。完全沒有解決SAX
和DOM
的缺點。
DOM4j
對比過dom4j
和JAXP
就會發現,JAXP
本質上還是將SAX
和DOM
當成兩套API來看待,而dom4j
就不是,它將SAX
和DOM
結合在一起使用,取長補短,並對原有的api進行了改造,在使用簡便性、效能、面向介面程式設計等方面都要優於JDK自帶的SAX
和DOM
。
以下通過使用例子和原始碼分析將作出說明。
專案環境
工程環境
JDK:1.8
maven:3.6.1
IDE:sts4
dom4j:2.1.1
建立專案
專案型別Maven Project,打包方式jar。
引入依賴
注意:dom4j
使用XPath
,必須引入jaxen
的jar包。
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- dom4j的jar包 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<!-- dom4j使用XPath需要的jar包 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 配置BeanUtils的包,這個我自定義工具類用的,如果只是簡單使用dom4j可以不引入 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
使用例子--生成xml檔案
本例子將分別使用dom4j
和JDK的DOM
介面生成xml檔案(使用JDK的DOM
介面時會使用JAXP
的API)。
需求
構建xml樹,新增節點,並生成xml檔案。格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<members>
<students>
<student name="張三" location="河南" age="18"/>
<student name="李四" location="新疆" age="26"/>
<student name="王五" location="北京" age="20"/>
</students>
<teachers>
<teacher name="zzs" location="河南" age="18"/>
<teacher name="zzf" location="新疆" age="26"/>
<teacher name="lt" location="北京" age="20"/>
</teachers>
</members>
生成xml檔案--使用w3c的DOM介面
主要步驟
通過
JAXP
的API獲得Document
物件,這個物件可以看成xml的樹;將物件轉化為節點,並新增在
Document
這棵樹上;通過
Transformer
物件將樹輸出到檔案中。
編寫測試類
路徑:test目錄下的cn.zzs.dom4j
。
注意:因為使用的是w3c
的DOM
介面,所以節點物件導的是org.w3c.dom
包,而不是org.dom4j
包。
@Test
public void test02() throws Exception {
// 建立工廠物件
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 建立DocumentBuilder物件
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 建立Document物件
Document document = documentBuilder.newDocument();
// 建立根節點
Element root = document.createElement("members");
document.appendChild(root);
// 新增一級節點
Element studentsElement = (Element)root.appendChild(document.createElement("students"));
Element teachersElement = (Element)root.appendChild(document.createElement("teachers"));
// 新增二級節點並設定屬性
Element studentElement1 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement1.setAttribute("name", "張三");
studentElement1.setAttribute("age", "18");
studentElement1.setAttribute("location", "河南");
Element studentElement2 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement2.setAttribute("name", "李四");
studentElement2.setAttribute("age", "26");
studentElement2.setAttribute("location", "新疆");
Element studentElement3 = (Element)studentsElement.appendChild(document.createElement("student"));
studentElement3.setAttribute("name", "王五");
studentElement3.setAttribute("age", "20");
studentElement3.setAttribute("location", "北京");
Element teacherElement1 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement1.setAttribute("name", "zzs");
teacherElement1.setAttribute("age", "18");
teacherElement1.setAttribute("location", "河南");
Element teacherElement2 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement2.setAttribute("name", "zzf");
teacherElement2.setAttribute("age", "26");
teacherElement2.setAttribute("location", "新疆");
Element teacherElement3 = (Element)teachersElement.appendChild(document.createElement("teacher"));
teacherElement3.setAttribute("name", "lt");
teacherElement3.setAttribute("age", "20");
teacherElement3.setAttribute("location", "北京");
// 獲取檔案物件
File file = new File("members.xml");
if(!file.exists()) {
file.createNewFile();
}
// 獲取Transformer物件
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
// 設定編碼、美化格式
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
// 建立DOMSource物件
DOMSource domSource = new DOMSource(document);
// 將document寫出
transformer.transform(domSource, new StreamResult(new PrintWriter(new FileOutputStream(file))));
}
測試結果
此時,在專案路徑下會生成members.xml
,檔案內容如下,可以看到,使用w3c
的DOM
介面輸出的內容沒有縮排格式。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<members>
<students>
<student age="18" location="河南" name="張三"/>
<student age="26" location="新疆" name="李四"/>
<student age="20" location="北京" name="王五"/>
</students>
<teachers>
<teacher age="18" location="河南" name="zzs"/>
<teacher age="26" location="新疆" name="zzf"/>
<teacher age="20" location="北京" name="lt"/>
</teachers>
</members>
生成xml檔案--使用dom4j的DOM介面
主要步驟
通過
DocumentHelper
獲得Document
物件,這個物件可以看成xml的樹;將物件轉化為節點,並新增在
Document
這棵樹上;通過
XMLWriter
物件將樹輸出到檔案中。
編寫測試類
路徑:test目錄下的cn.zzs.dom4j
。通過對比,可以看出,dom4j
的API相比JDK的還是要方便很多。
注意:因為使用的是dom4j
的DOM
介面,所以節點物件導的是org.dom4j
包,而不是org.w3c.dom
包(dom4j
一個很大的特點就是改造了w3c
的DOM
介面,極大地簡化了我們對節點的操作)。
@Test
public void test02() throws Exception {
// 建立Document物件
Document document = DocumentHelper.createDocument();
// 新增根節點
Element root = document.addElement("members");
// 新增一級節點
Element studentsElement = root.addElement("students");
Element teachersElement = root.addElement("teachers");
// 新增二級節點並設定屬性,dom4j改造了w3c的DOM介面,極大地簡化了我們對節點的操作
studentsElement.addElement("student").addAttribute("name", "張三").addAttribute("age", "18").addAttribute("location", "河南");
studentsElement.addElement("student").addAttribute("name", "李四").addAttribute("age", "26").addAttribute("location", "新疆");
studentsElement.addElement("student").addAttribute("name", "王五").addAttribute("age", "20").addAttribute("location", "北京");
teachersElement.addElement("teacher").addAttribute("name", "zzs").addAttribute("age", "18").addAttribute("location", "河南");
teachersElement.addElement("teacher").addAttribute("name", "zzf").addAttribute("age", "26").addAttribute("location", "新疆");
teachersElement.addElement("teacher").addAttribute("name", "lt").addAttribute("age", "20").addAttribute("location", "北京");
// 獲取檔案物件
File file = new File("members.xml");
if(!file.exists()) {
file.createNewFile();
}
// 建立輸出格式,不設定的話不會有縮排效果
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
// 獲得XMLWriter
XMLWriter writer = new XMLWriter(new FileWriter(file), format);
// 列印Document
writer.write(document);
// 釋放資源
writer.close();
}
測試結果
此時,在專案路徑下會生成members.xml
,檔案內容如下,可以看出dom4j
輸出檔案會進行縮排處理,而JDK的不會:
<?xml version="1.0" encoding="UTF-8"?>
<members>
<students>
<student name="張三" age="18" location="河南"/>
<student name="李四" age="26" location="新疆"/>
<student name="王五" age="20" location="北京"/>
</students>
<teachers>
<teacher name="zzs" age="18" location="河南"/>
<teacher name="zzf" age="26" location="新疆"/>
<teacher name="lt" age="20" location="北京"/>
</teachers>
</members>
使用例子--解析xml檔案
需求
- 解析xml:解析上面生成的xml檔案,將學生和老師節點按以下格式遍歷打印出來(當然也可以再封裝成物件返回給呼叫者,這裡就不擴充套件了)。
student:name=張三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20
dom4j
結合XPath
查詢指定節點
主要步驟
通過
SAXReader
物件讀取和解析xml檔案,獲得Document
物件,即xml樹;呼叫
Node
的方法遍歷列印xml樹的節點;使用
XPath
查詢指定節點。
測試遍歷節點
考慮篇幅,這裡僅給出一種節點遍歷方式,專案原始碼中還給出了其他的幾種。
/**
* 測試解析xml
*/
@Test
public void test03() throws Exception {
// 建立指定檔案的File物件
File file = new File("members.xml");
// 建立SAXReader
SAXReader saxReader = new SAXReader();
// 將xml檔案讀入成document
Document document = saxReader.read(file);
// 獲得根元素
Element root = document.getRootElement();
// 遞迴遍歷節點
list1(root);
}
/**
* 遞迴遍歷節點
*/
private void list1(Element parent) {
if(parent == null) {
return;
}
// 遍歷當前節點屬性並輸出
printAttr(parent);
// 遞迴列印子節點
Iterator<Element> iterator2 = parent.elementIterator();
while(iterator2.hasNext()) {
Element son = (Element)iterator2.next();
list1(son);
}
}
測試結果如下:
-------第一種遍歷方式:Iterator+遞迴--------
student:name=張三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20
測試XPath獲取指定節點
@Test
public void test04() throws Exception {
// 建立指定檔案的File物件
File file = new File("members.xml");
// 建立SAXReader
SAXReader saxReader = new SAXReader();
// 將xml檔案讀入成document
Document document = saxReader.read(file);
// 使用xpath隨機獲取節點
List<Node> list = document.selectNodes("//members//students/student");
// List<Node> list = xmlParser.getDocument().selectSingleNode("students");
// 遍歷節點
Iterator<Node> iterator = list.iterator();
while(iterator.hasNext()) {
Element element = (Element)iterator.next();
printAttr(element);
}
}
測試結果如下:
student:age=18,location=河南,name=張三
student:age=26,location=新疆,name=李四
student:age=20,location=北京,name=王五
XPath語法
利用XPath
獲取指定節點,平時用的比較多,這裡列舉下基本語法。
表示式 | 結果 |
---|---|
/members | 選取根節點下的所有members子節點 |
//members | 選取根節點下的所有members節點 |
//students/student[1] | 選取students下第一個student子節點 |
//students/student[last()] | 選取students下的最後一個student子節點 |
//students/student[position()<3] | 選取students下前兩個student子節點 |
//student[@age] | 選取所有具有age屬性的student節點 |
//student[@age='18'] | 選取所有age屬性為18的student節點 |
//students/* | 選取students下的所有節點 |
//* | 選取文件中所有節點 |
//student[@*] | 選取所有具有屬性的節點 |
//members/students\ | //members/teachers |
原始碼分析
本文會先介紹dom4j
如何將xml元素抽象成具體的物件,再去分析dom4j
解析xml檔案的過程(注意,閱讀以下內容前需要了解和使用過JDK自帶的DOM
和SAX
)。
dom4j節點的類結構
先來看下一個完整xml的元素組成,可以看出,一個xml檔案包含了Document
、Element
、Comment
、Attribute
、DocumentType
、Text
等等。
DOM
的思想就是將xml元素解析為具體物件,並構建樹形資料結構。基於此,w3c
提供了xml元素的介面規範,dom4j
基本借用了這套規範(如下圖),只是改造了介面的方法,使得我們操作時更加簡便。
SAXReader.read(File file)
通過使用例子可知,我們解析xml檔案的入口是SAXReader
物件的read
方法,入參可以是檔案路徑、url、位元組流、字元流等,這裡以傳入檔案路徑為例。
注意:考慮篇幅和可讀性,以下程式碼經過刪減,僅保留所需部分。
public Document read(File file) throws DocumentException {
//不管是URI,path,character stream還是byte stream,都會包裝成InputSource物件
InputSource source = new InputSource(new FileInputStream(file));
if (this.encoding != null) {
source.setEncoding(this.encoding);
}
//下面這段程式碼是為了設定systemId,當傳入URI且沒有指定字元流和位元組流時,可以通過systemId去連線URL並解析
//如果一開始傳入了字元流或位元組流,這個systemId就是可選的
String path = file.getAbsolutePath();
if (path != null) {
StringBuffer sb = new StringBuffer("file://");
if (!path.startsWith(File.separator)) {
sb.append("/");
}
path = path.replace('\\', '/');
sb.append(path);
source.setSystemId(sb.toString());
}
//這裡呼叫過載方法解析InputSource物件
return read(source);
}
SAXReader.read(InputSource in)
看到這個方法的程式碼時,使用過JDK的SAX
的朋友應該很熟悉,沒錯,dom4j
也是採用事件處理的機制來解析xml。其實,只是這裡設定的SAXContentHandler
已經實現好了相關的方法,這些方法共同完成一件事情:構建xml樹。明白這一點,應該就能理解dom4j
是如何解決SAX
和DOM
的缺點了。
注意:考慮篇幅和可讀性,以下程式碼經過刪減,僅保留所需部分。
public Document read(InputSource in) throws DocumentException {
// 這裡會呼叫JAXP介面獲取XMLReader實現類物件
XMLReader reader = getXMLReader();
reader = installXMLFilter(reader);
// 下面這些操作,是不是和使用JDK的SAX差不多,dom4j也是使用了事件處理機制。
// EntityResolver:通過實現resolveEntity方法,當解析xml需要引入外部資料來源時觸發,可以重定向到本地資料來源或進行其他操作。
EntityResolver thatEntityResolver = this.entityResolver;
if (thatEntityResolver == null) {
thatEntityResolver = createDefaultEntityResolver(in
.getSystemId());
this.entityResolver = thatEntityResolver;
}
reader.setEntityResolver(thatEntityResolver);
// 下面的SAXContentHandler繼承了DefaultHandler,即實現了EntityResolver, DTDHandler, ContentHandler, ErrorHandler等介面
// 其中最重要的是ContentHandler介面,通過實現startDocument、endDocument、startElement、endElement等方法,當dom4j解析xml檔案到指定元素型別時,可以觸發我們自定義的方法。
// 當然,dom4j已經實現了ContentHandler的方法。具體實現的方法內容為:在解析xml時構建xml樹
SAXContentHandler contentHandler = createContentHandler(reader);
contentHandler.setEntityResolver(thatEntityResolver);
contentHandler.setInputSource(in);
boolean internal = isIncludeInternalDTDDeclarations();
boolean external = isIncludeExternalDTDDeclarations();
contentHandler.setIncludeInternalDTDDeclarations(internal);
contentHandler.setIncludeExternalDTDDeclarations(external);
contentHandler.setMergeAdjacentText(isMergeAdjacentText());
contentHandler.setStripWhitespaceText(isStripWhitespaceText());
contentHandler.setIgnoreComments(isIgnoreComments());
reader.setContentHandler(contentHandler);
configureReader(reader, contentHandler);
// 使用事件處理機制解析xml,處理過程會構建xml樹
reader.parse(in);
// 返回構建好的xml樹
return contentHandler.getDocument();
}
SAXContentHandler
通過上面的分析,可知SAXContentHandler
是dom4j
構建xml樹的關鍵。這裡看下它的幾個重要方法和屬性。
startDocument()
// xml樹
private Document document;
// 節點棧,棧頂存放當前解析節點(節點解析結束)、或當前解析節點的父節點(節點解析開始)
private ElementStack elementStack;
// 節點處理器,可以看成節點開始解析或結束解析的標誌
private ElementHandler elementHandler;
// 當前解析節點(節點解析結束)、或當前解析節點的父節點(節點解析開始)
private Element currentElement;
public void startDocument() throws SAXException {
document = null;
currentElement = null;
// 清空節點棧
elementStack.clear();
// 初始化節點處理器
if ((elementHandler != null)
&& (elementHandler instanceof DispatchHandler)) {
elementStack.setDispatchHandler((DispatchHandler) elementHandler);
}
namespaceStack.clear();
declaredNamespaceIndex = 0;
if (mergeAdjacentText && (textBuffer == null)) {
textBuffer = new StringBuffer();
}
textInTextBuffer = false;
}
startElement(String,String,String,Attributes)
public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes attributes) throws SAXException {
if (mergeAdjacentText && textInTextBuffer) {
completeCurrentTextNode();
}
QName qName = namespaceStack.getQName(namespaceURI, localName,
qualifiedName);
// 獲取當前解析節點的父節點
Branch branch = currentElement;
if (branch == null) {
branch = getDocument();
}
// 建立當前解析節點
Element element = branch.addElement(qName);
addDeclaredNamespaces(element);
// 新增節點屬性
addAttributes(element, attributes);
//將當前節點壓入節點棧
elementStack.pushElement(element);
currentElement = element;
entity = null; // fixes bug527062
//標記節點解析開始
if (elementHandler != null) {
elementHandler.onStart(elementStack);
}
}
endElement(String, String, String)
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
if (mergeAdjacentText && textInTextBuffer) {
completeCurrentTextNode();
}
// 標記節點解析結束
if ((elementHandler != null) && (currentElement != null)) {
elementHandler.onEnd(elementStack);
}
// 當前解析節點從節點棧中彈出
elementStack.popElement();
// 指定為棧頂節點
currentElement = elementStack.peekElement();
}
endDocument()
public void endDocument() throws SAXException {
namespaceStack.clear();
// 清空節點棧
elementStack.clear();
currentElement = null;
textBuffer = null;
}
以上,dom4j
的原始碼分析基本已經分析完,其他具體細節後續再做補充。
參考以下資料:
淺析SAX,DOM,JAXP,JDOM與DOM4J之間的關係
本文為原創文章,轉載請附上原文出處連結:https://github.com/ZhangZiSheng001/dom4j-demo