JavaSE(七)XML與配置文件

資源文件

  通過一個Class對象獲取該類的資源,如Test.class.getResource(fileName)返回資源的URL或Test.class.getResourceAsStream(fileName)返回資源的輸入流,fileName是相對於類所在的目錄的相對路徑,無論哪種系統,fileName各級目錄間用/分割,如果fileName是以/開始的絕對路徑,那麼根目錄是與包的定義方式一樣的根目錄,也就是classpath目錄。

屬性映射

  屬性映射Properties類是一種儲存鍵值對的數據結構,它的鍵和值都是字符串,相對於Map,它很容易讀寫文件

// 該方法間接繼承自Map,所以Map的方法他都有。
public class Properties extends Hashtable<Object,Object> {
  public Properties(); 
  public Properties(Properties defaults); // defaults對應默認值,在當前屬性映射裏面找不到的時候會到default裏面去找;
  public synchronized Object setProperty/put(String key, String value); // 設置值;
  public String getProperty(String key); // 如果沒有對應值,則看defaults裏面有對應默認值不,都沒有返回null;
  public Stream getProperty(String key, String default); // 在調用getProperty(key)返回null時返回default;
  public synchronized void load(Reader reader/InputStream is); // 加載屬性值;
  public void store(Writer writer/OutputStream os, String comments); // comments是文件頭的註釋,存儲時會自動加上註釋符號#;
  public synchronized void loadFromXML(InputStream in); // 從xml文件加載屬性,xml文件必須爲<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  public void storeToXML(OutputStream os, String comment); // 存儲爲xml格式
  public Set<String> stringPropertyNames(); // 誰有key的集合; 
}

Preferences存儲

  一般操作系統中都有一個樹狀的中心知識庫,每一節點都對應一個存放鍵值對的表,這些表數據存儲在本地機器上,如windows存儲在註冊表中,linux存儲在一個隱藏文件中,我們一般不手動去更改這些信息,而是用api去更改他們。系統中有多個並行的樹,其中分爲系統樹和用戶樹,系統樹一個系統只一顆,而用戶樹每個操作系統的登錄用戶都只有一顆,我們用preference存放配置信息,區分系統樹和用戶樹就能區分公用配置和各個登錄用戶的獨特配置。我們一般讓一個應用有一個單獨的節點,所以習慣用該應用的包路徑作爲對應的節點路徑。

public abstract class Preferences {
	public static Preferences userRoot(); // 獲取操作系統當前登錄用戶的用戶樹的根;
	public static Preferences systemRoot(); // 獲取操作系統系統樹的根;
	public static Preferences userNodeForPackage(Class c); // 獲取相對於當前登錄用戶樹根以c所在包相應路徑爲路徑的節點,無節點就創建節點;
	public static Preferences systemNodeForPackage(Class c); // 獲取相對於系統樹根以c所在包相應路徑爲路徑的節點,無節點就創建節點;
	public Preferences node(String path); // 獲取節點,path以/爲分隔符,如果以/開頭則爲相對於調用該方法的節點的根(用戶樹或系統樹根)開始,否則相對於調用節點開始;
	public abstract void removeNode(); // 移除當前noed;
	public void put(String key, String value); // 直接就進行了持久化保存;
	public void putXxx(String key, xxx value); // xxx爲基本類型或byte[], byte[]對應的Xxx爲ByteArray;
	public String get(String key, String default); // 獲取當前節點相對鍵值的值,如果值不存在或類型不匹配或系統不支持中心知識庫就返回default;
	public xxx getXxx(String key, xxx default); // 
	public String remove(String key); // 從當前節點刪除key對應的鍵值對;
	public String[] keys(); // 該節點所有的鍵值;
	public void clear(); // 清除節點的鍵值;
	public void exportSubtree(OutputStream os); // 導出該節點及其子節點相關數據以xml格式保存;
	public void exportNode(OutpurStream os); // 導出該節點相關數據並以xml格式保存;
	public void importPreference(InputStream is); // 導入鍵值對,導入導出可以用於應用遷移到另一個平臺;
	public String[] childrenNames(); // 當前節點的直接子節點名;
	public Preferences parent(); // 獲取父節點,如果當前節點已經是root節點則返回null;
	public String name(); // 返回節點名;
	public abstract String absolutePath(); // 返回相對於root的絕對路徑;
	public boolean nodeExists(String pathName); // pathName爲路徑的節點是否存在;
	public abstract boolean isUserNode(); // 
	public abstract void addPreferenceChangeListener(PreferenceChangeListener pcl);
	public abstract void removePreferenceChangeListener(PreferenceChangeListener pcl);
	public abstract void addNodeChangeListener(NodeChangeListener ncl);
	public abstract void removeNodeChangeListener(NodeChangeListener ncl);
}

XML解析

  Java解析XML的方式分爲基於對象與基於事件兩種方式,其常用的框架主要有四種,DOM解析、SAX解析、StAX解析、JDOM解析與DOM4J解析,其中DOM與SAX已包含在JRE內置包中,而JDOM與DOM4J需要引入第三方包,如果不是特殊需求,推薦使用DOM4J,其性能最好。
  基於事件的框架,其思路爲,一面解析文件,一面處理業務,每當有事件發生(如開始讀取文檔、結束讀取文檔、讀到某個標籤、結束某個標籤等),就回調用戶實現的處理器進行業務處理。基於對象的框架凌駕於基於事件的框架之上,讓基於事件的框架回調的處理器不進行業務處理,而將事件記錄成一個對象,最後由用戶實現對記錄的對象進行業務處理,這就形成了基於對象的框架

XPath語法規則

  XPath是一門在XML文檔中查找信息的語言,它可用來在XML文檔中對元素和屬性進行遍歷。XPath表達式的組成分爲路徑分隔符和步,型如…/step1//step2/setp3。另外可以用 | 連接兩個xpath表達式取兩者的並集。實際上XPath表達式可以表示數值、字符串、boolean以及節點集,在DOM做XPath解析的時候可以把一個XPath表達式解析成以上幾種類型
  路徑分隔符有 / 和 //,/ 連接的兩個step具有直接的父子關係,而 // 連接的兩個step只需要具有祖先後代關係。路徑可分爲絕對路徑與相對路徑,絕對路徑以 / 或者 // 開頭,相對路徑以步開頭,以 / 開頭接的step必須是根下的第一級step才能匹配,而以 // 開頭接的step是根下的任一級step都可以匹配。
  步的格式爲軸名稱::節點測試[謂語],有 . 與 … 可以認爲是特殊的步,表示當前節點與父節點。
  軸名稱有self(當前節點)、ancestor(祖先節點)、descendant(後代節點)、ancestor-or-self、descendant-or-self、parent(父節點)、child(子節點)、preceding(文檔中位於當前節點之前(開始標籤之前)的完整節點(開始標籤與結束標籤都在當前節點之前的節點))、preceding-sibling(preceding中與當前節點是兄弟關係的節點)、following(文檔中位於當前節點之後(結束標籤之後)的完整節點(開始標籤和結束標籤都在當前節點之後的節點))、following-sibling(following中與當前節點是兄弟關係的節點)、attribute(當前節點的所有屬性子節點)、namespace(本座也沒明白這個軸的意義),attribute與namespace以外的軸不會包含attribute節點,當軸名稱爲child時,可以省略child::,當爲attribute時可以用@代替attribute::
  節點測試對軸解析出的節點進行過濾,最常用的節點測試是節點名,用於根據節點名過濾,另外有 *, 當軸爲attribute時表示篩選出屬性節點,當軸爲其他時表示篩選出element節點,text()表示篩選出文本節點,processing-instruction()表示篩選出指令節點,node()表示篩選出任意類型的節點,comment()表示篩選出註釋節點。
  謂語負責對節點測試得到的節點集依次遍歷進行過濾(本次遍歷我們暫時叫着上下文,遍歷到某個節點就把這個節點暫時叫着當前節點),謂語中可以有常量、運算符、函數以及子xpath表達式(相對路徑都是相對於當前節點的,絕對路徑依然是相對於文檔節點,xpath的值是一個nodeset類型)。當謂語的值爲數值類型n時,表示index爲n的當前節點會被選中(等同於position() = n),當謂語的值不是數值類型時,用boolean函數作用於謂語的值爲真的當前節點會被選中
  常量有數值常量與字符串常量,數字常量就爲數字字面值,字符串常量用雙引號或者單引號包裹,因爲不用字符串包裹且不是函數(不接括號)的字符串表示xpath表達式,所以沒有boolean類型常量(true會被認爲是xpath的步)。數字常量有些特殊的值,非數值NaN(不能轉換爲數字的字符串非要轉換爲字符串得到,轉換爲字符串爲’NaN’)、正無窮大Indinity(正數除以0得到,轉換爲字符串爲’Infinity’)、負無窮大-Infinity(負數除以0得到,轉換爲字符串爲’-Infinity’)。NaN參與任何數值計算都得到NaN,任意兩個NaN都不相等,如 ‘a’ + 1 != ‘a’ + 1返回真,判斷一個數值是否爲NaN的方法是將數值用string函數處理後看是否爲字符串’NaN’;任意兩個正無窮都相等,任意兩個負無窮也都相等,那麼判斷一個數值是否爲正無窮可以與 1 div 0比較,如 3 div 0 = 1 div 0 返回真,也可以用string函數處理後的值是否爲’Infinity’來判斷是否爲正無窮,負無窮同理。
  xpath中的運算符有數值運算(+、-、*、div、mod)、比較運算符(=、!=、>、<、>=、<=)和邏輯運算符(or、and),可以用小括號來定義運算優先級。對於邏輯運算,先對參與運算的值調用boolean函數進行處理後再進行運算;對於數值運算以及比較運算中的 >、<、>=、<=,先對參與運算的值調用number函數進行處理後再進行運算;對於比較運算中 = 與 !=,如果有boolean類型參與運算,那麼把非boolean類型操作數調用boolean函數(所以 true() = 2的結果是真)後參與運算,否則如果有number類型參與運算,那麼將非number類型操作數調用number函數後參與運算。在所有比較運算中(=、!=、>、<、>=、<=),如果有節點集參與運算,那麼就是遍歷節點集中的每一個節點的值來運算,只要有一次運算結果爲真,那麼本次比較的結果就爲真。如果兩個結果集進行比較,那麼可能比較符爲任何比較符結果都爲真
  xpath中常用在謂語中的函數(? 表示該參數可以有或者沒有(沒有默認爲當前節點),* 表示該參數可以沒有或者有多個,nodeset表示參數只能是節點集,any表示參數可以是任何類型,string / number / boolean表示參數爲任何類型並且會首先調用string(any) / number(any) / boolean(any)將參數處理爲string / number / boolean類型):

  1. 節點集相關
    • number postion() 返回當前節點在當前上下文中的位置號數,位置號數從1開始
    • number last() 返回最後一個節點在當前上下文中的位置號數
    • number count(nodeset) 返回節點集的節點數量
    • nodeset id(any) 根據在DTD中聲明爲ID類型的標識符選擇元素,返回一個節點集
    • string local-name(nodeset?) 返回節點集中第一個節點的不帶前綴的節點名
    • string namespace-uri(nodeset?) 返回節點集中第一個節點的命名空間uri
    • string name(nodeset?) 返回節點集中第一個節點的prefix:local-name
  2. 字符串相關
    • string string(any?) boolean返回’true’或’false’,數字返回對應的字符串,字符串原值返回,nodeset返回第一個節點的值
    • string concat(string, string, string*) 連接字符串
    • boolean start-with(string, string) 第一個字符串參數是否是以第二個字符串參數開頭
    • boolean contains(string, string) 第二個字符串參數是否是第一個字符串參數的子串
    • string substring-after/before(string, string) 第二個參數在第一個參數中第一次出現的位置前/後的子串
    • string substring(string, number, number?) 子串,第三個參數默認爲第一個參數的長度
    • number string-length(string?) 返回string的長度
    • string normalize-space(string?) 清除string頭尾的空白字符並且把中間連續的空白字符替換爲一個再返回
    • translate(string1, string2, string3) 將string1中的string2替換爲string4(當string3的長度大於string2的長度時,string4取string3的前string-leng(string2)個字符,否則string4就取string3),如translate(‘abab’, ‘ab’, ‘cde’) = ‘cdcd’,translate(‘abab’, ‘ab’, ‘c’) = ‘cc’,translate(‘abab’, ‘ab’, ‘cd’) = ‘cdcd’
  3. boolean相關
    • boolean true() 真
    • boolean false() 假
    • boolean boolean(any) 數字非0爲真,集合非空爲真,字符串非空串爲真
    • boolean not(boolean) 取相反的值
    • boolean lang(string) 檢查當前節點的xml:lang屬性是否爲string的值
  4. 數字相關
    • number number(any?) 數字返回其原值,布爾值true返回1,false返回0;節點集首先轉換成字符串,字符串轉換成對應的數字,默認爲當前節點
    • number sum(nodeset) 對節點集nodeset中的所有節點應用number()函數後返回和
    • number round(number) 返回四捨五入的值
    • number ceiling(number) 返回不小於數字number的最小整數
    • number floor(number) 返回不大於數字number的最大整數

  解析路徑從前往後依次解析,每一級step的解析都是對上一級step解析得到的所有節點依次 獨立 處理後合併

/**
	代碼模擬步的處理過程
	@param:parentStepNodes 上一step篩選得到的節點集
	@return: 本次step得到的節點集
/*
Set<Node> selectNodes(Set<Node> parentStepNodes) {
	Set<Node> returnNodes = new HashSet<>();
	for (Node dealNode: parentStepNodes) {
		Set<Node> thisStepNodes = axis(dealNode); // 根據dealNode與軸得到節點集
		thisStepNodes = nodeTest(thisStepNodes); // 保留符合測試節點
		for (Node currentNode: thisStepNodes) { 
			Object predicateValue = redicate(currentNode); // 謂語運算值
			if (predicateValue instanceof Integer) { // 當謂語值爲整數時
				if (thisStepNodes.indexOf(currentNode) == predicateValue) { 如果當前節點在測試節點中的位置爲謂語值時
					thisStepNodes.remove(currentNode);
				}
			} else if (predicateValue instanceof String) {
				if (predicateValue == '') {
					thisStepNodes.remove(currentNode);
				}
			} else if (predicateValue instanceof Boolean) {
				if (!predicateValue) {
					thisStepNodes.remove(currentNode);
				}
			} else if (predicateValue instanceof Set) {
				if (predicateValue.size() == 0) {
					thisStepNodes.remove(currentNode);
				}
			} else {
				...
			}
		}

		returnNodes.addAll(thisStepNodes);
	}
	return returnNodes;
}

  實例分析

<a id="a1">
	<b id="b1">
		<c id="c1">
			<d>
			</d>
		</c>
		<c id="c2"> 35 </c>
	</b>
	<b id="b2" battr1="35">
		<c id="c3">20</c>
	</b>
</a>
xpath 解析結果(節點的id) 解析分析
/a/b/c[1] c1、c3 /a/b得到兩個節點b1、b2,解析b1的子節點並且節點名爲c的第一個得到c1(c2被丟棄),同樣解析b2得到c3,所以最後結果是c1、c3
/a/b[1]/c c1 、c2 /a/b[1]得到一個節點b1(b2被丟棄),解析b1的子節點並且節點名爲c的所有節點得到c1、c2
/a/b[1]/c[1] c1 /a/b[1]得到一個節點b1(b2被丟棄),解析b1的子節點並且節點名爲c的第一個得到c1(c2被丟棄)
/a/b[1]/c[1] c1 /a/b[1]得到一個節點b1(b2被丟棄),解析b1的子節點並且節點名爲c的第一個得到c1(c2被丟棄)
/a/b[1]/…/b b1、b2 /a/b[1]得到一個節點b1(b2被丟棄),解析b1的父節點得到a1,解析a1的子節點中名爲b的節點b1、b2
//b/c c1 、c2、c3 //b獲取所有名稱爲b的節點,得到b1,b2,解析b1的子節點並且節點名爲c的節點得到c1、c2,同樣解析b2得到c3,所以最後結果是c1、c2、c3
/a//c c1、c2、c3
/a/b/text() [2 - 1] 兩個文本節點 /a/b得到b1與b2,解析b1的子節點中爲文本節點的節點中的第一個,同理得到b2的第一個文本節點,故最後得到的是b1下的第一個文本節點和b2下的第一個文本節點
/a/b/c[self::*>30] c2 /a/b找打b1與b2,解析b1的子節點中節點名爲c並且節點的值大於30的節點得到c2,同理得到對b2的解析爲空,兩者並集爲c2
/a/b[c>30] b1 /a得到a1,a1下所有節點名爲b且擁有子節點c的值大於30的節點
/a/b[c>30]/c c1、c2 /a/b[c>30]得到b1,解析b1的子節點中名爲c的元素
/a/b/c[self::*>30] | /a/b[c>30] c2、b1 /a/b/c[self::*>30]得到c2,/a/b[c>30]得到b1,兩者並集爲c2、b1
/a/b[@battr1>20] b1 /a得到a1,a1的子節點中名爲b且具有battr1屬性且屬性值大於20的節點
/a/b[@battr1] b1 /a得到a1,a1的子節點中名爲b且具有battr1屬性的節點
/a/b/c/*[namespace-uri() = ‘http://www.wanglang.com’ and local-name() = ‘d’] d1
/a/b/c[/a/b/c/*[/a/b[c>30]] c1、c2、c3 這是嵌套的xpath, /a/b[c>30]的結果是b1,所以/a/b/c[這裏面是一個元素個數爲1(b1)的節點集,節點集非空判斷爲真]

DOM(Document Object Model)解析方式

  DOM解析方式會讀取整個XML文件並將其解析爲一個Document對象(實現了org.w3c.dom.Document接口),通過對這個Document對象的操作,來實現對XML文檔數據的操作。DOM強制使用樹模型來訪問XML文檔中的信息,是解析XML的官方標準(由W3C定義)。org.w3c.dom.Document是個接口,Java提供了工廠方法來獲得Document實例。DOM解析方式對XML的解析其實就是對Document的分析
  創建一個Document對象,首先需要通過DocumentBuilderFactory的靜態方法newInstance()獲取一個DocumentBuilderFactory對象,然後通過該工廠的newDocumentBuilder方法獲取一個Document的解析器DocumentBuilder對象,最後通過解析器DocumentBuilder的parse方法將一個xml文件解析爲一個Document對象,或newDocument方法構建一個空的Document對象。如果在解析XML時有特殊需求,如忽略逃逸字符等,可以通過設置工廠的某些屬性來實現
  將一個Document保存爲xml文件,首先需要通過TransformerFactory的靜態方法newInstance()獲取一個TransformerFactory對象,然後通過該工廠的newTransformer()方法獲取一個轉換器Transformer對象,最後通過轉換器對象的transform方法將一個source轉換爲result(document保存到流中時,Source爲DomSource,Result爲StreamSource,其實該方法也可以從流中讀取爲document,此時Source爲StreamSource,Result爲DomResult,DomResult的getNode就是document節點)。

// 解析器工廠
public abstract class DocumentBuilderFactory {
    public static DocumentBuilderFactory newInstance();
    public abstract DocumentBuilder newDocumentBuilder();
    
    ... 另外有一系列函數,用來爲創建的解析器的解析細節設置控制參數,比如忽略註釋,是否驗證DTD等,
    	默認是不支持命名空間的,所以建議創建DocumentBuilderFactory對象後先調用其setNamespaceAware(true)方法來支持命名空間;
}

// 解析器
public abstract class DocumentBuilder {
    public void reset();  // 將此 DocumentBuilder 重置爲其原始配置
    public Document parse(InputStream is / String uri / File f);  // 解析出一個新的Document對象
    public abstract Document newDocument(); // 獲取一個新的Document對象
    public abstract void setEntityResolver(EntityResolver er);  // 自定義處理實體
    public abstract void setErrorHandler(ErrorHandler eh); 
    
    ... 另外有一系列控制解析相關的函數比較少用(都是讀函數,沒有寫函數,這些函數的控制參數在工廠創建解析器時提供,也由解析器工廠配置)
}

  DOM模式基於樹模型,既然樹模型就會有節點,這些節點被分爲了多個類型。整個文檔是一個文檔節點,每個XML標籤是一個元素節點、包含在XML元素中的文本是文本節點、每一個XML 屬性是一個屬性節點、註釋屬於註釋節點等。Java中Node有子類Document、DocumentType、Element、Attr、Text、CDATASection、Comment、EntityReference、Entity、ProcessingInstruction、DocumentFragment、Notation,其中有些節點類型可以有子節點,有些沒有子節點。
  Document是整個文檔,對於xml文件裏面第一層的所有節點都是其子節點。Attr與Text都是對應Element的子節點

// 一個節點
public interface Node {
    public String getNodeName();
    public String getNodeValue();
    public short getNodeType();
    public NamedNodeMap getAttributes();
    public boolean hasChildNodes();
    public String getNamespaceURI();
    public boolean hasAttributes();
    public String getTextContent();
    public boolean isSameNode(Node other);
    public boolean isDefaultNamespace(String namespaceURI);

	...另外還有一系列獲取父節點、子節點、兄弟節點的方法
	...另外還有一系列的修改信息、增加刪除子節點等的方法,用得比較少
}

// 就是一個Node集合
public interface NodeList {
    public Node item(int index);
    public int getLength();
}

// 在解析時一般只會用到讀方法
public interface Document extends Node {
    public DocumentType getDoctype(); // 獲取doctype節點
    public DOMImplementation getImplementation();
    public Element getDocumentElement(); // 一個XML只能有一個根元素,所以一個document也只會有一個根元素,該方法用來獲取根元素
    public NodeList getElementsByTagName(String tagname); // 獲取爲標籤爲tagname的element節點
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName);
    public Element getElementById(String elementId);
    public String getInputEncoding();
    public String getXmlEncoding();
    public boolean getXmlStandalone();
    public String getXmlVersion();
    public boolean getStrictErrorChecking();
    public String getDocumentURI();
    public DOMConfiguration getDomConfig();
    public void normalizeDocument();
    
    ... 還有一系列非讀方法比較少用
}

// 對應一個標籤
public interface Element extends Node {
    public String getTagName(); // 標籤名字    
    public boolean hasAttribute(String name);
    public String getAttribute(String name); // 獲取屬性值,屬性節點的值    
    public String getAttributeNS(String namespaceURI,  String localName);
    public boolean hasAttributeNS(String namespaceURI, String localName);
    public Attr getAttributeNode(String name); // 獲取屬性節點    
    public Attr getAttributeNodeNS(String namespaceURI, String localName);
    public NodeList getElementsByTagName(String name); // 獲取該element內部標籤爲name的element節點
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName);
    public TypeInfo getSchemaTypeInfo();
    
    ... 還有一系列非讀方法比較少用
} 

  DOM支持XPath操作,首先由XPathFactory的靜態方法newInstance創建工廠對象,然後調用工廠對象的newXPath方法獲取一個XPath對象,最後由XPath對象的evaluate方法來解析XPath表達式(也可以由XPath對象的compile方法創建一個XPathExpression對象,然後由XPathExpression對象的evaluate方法來解析)。DOM工廠創建DOM時默認是關閉了命名空間的解析功能的,所以在使用時需要打開其命名空間解析功能(在創建DocumentBuilderFactory對象後調用其setNamespaceAware(true)方法)。

public abstract class XPathFactory {
    public static XPathFactory newInstance(); // 創建工廠實例
    public abstract XPath newXPath(); // 創建XPath實例
    public abstract void setFeature(String name, boolean value);
    public abstract boolean getFeature(String name);
}

public interface XPath {
    public void reset(); // 還原爲初試配置
    public XPathExpression compile(String expression); // 得到一個XPath表達式對象
    public Object evaluate(String expression, Object item, QName returnType); // 同compile(expression).evaluate(item, returnType)
    public String evaluate(String expression, Object item); // 同compile(expression).evaluate(item)
    public Object evaluate(String expression, InputSource source, QName returnType); // 同compile(expression).evaluate(source, returnType)
    public String evaluate(String expression, InputSource source); // 同compile(expression).evaluate(source)
}

public interface XPathExpression {
	// 當returnType取XPathConstants.NODESET時返回NodeList
	// 當returnType取XPathConstants.NODE時返回NodeList的第一個Node
	// 當returnType取XPathConstants.BOOLEAN時返回Boolean
	// 當returnType取XPathConstants.STRING時返回String
	// 當returnType取XPathConstants.NUMBER時返回Double
	// 可以根據returnType強轉返回值類型
    public Object evaluate(Object item, QName returnType);  // item爲上下文,相對路徑就相對於該上下文,如取document或者任意一個element
    public String evaluate(Object item);
    public Object evaluate(InputSource source, QName returnType); // 通過source構建上下文
    public String evaluate(InputSource source);
}

SAX(Simple APIs for XML)解析方式

  SAX基於事件的解析,解析器在一次讀取XML文件中根據讀取的數據產生相應的事件,由應用程序實現相應的事件處理邏輯
  SAX解析首先需要通過解析器工廠SAXParserFactory的newInstance方法獲取一個解析工廠實例SAXParserFactory,然後通過該解析器工廠實例的newSAXParser方法創建解析器SAXParser,最後通過解析器實例SAXParser的parse方法解析XML並在解析過程中回調用戶定義的處理器進行處理。SAX解析的關鍵就是編寫處理器(處理器繼承自DefaultHandler或者HandlerBase)

javax.xml.parsers

public abstract class SAXParserFactory {
    public static SAXParserFactory newInstance();
    public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader);
    public abstract SAXParser newSAXParser();
    
	... 另外有一系列函數,用來爲創建的解析器的解析細節設置控制參數
}    

public abstract class SAXParser {
    public void reset(); // 將解析器復位到最初狀態
    public void parse(InputStream is / String uri / File file / InputSource is, HandlerBase hb / DefaultHandler dh);

	... 另外有一系列控制解析相關的函數比較少用
}

// 事件處理器
public class DefaultHandler implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler {
	// ContentHandler
    public void startDocument (); // 開始解析文件時觸發
    public void endDocument (); // 解析文件結束時觸發
    public void startElement (String uri, String localName, String qName, Attributes attributes);  // 開始一個標籤時觸發
    		// uri代表命名空間的標識符,當元素沒有命名空間或者解析器的命名空間支持特性沒有打開時是空串,如http://www.namespace.com;
    		// localName代表沒有前綴的本地名,當支持命名空間特性沒有打開時是空串,如tag-name;
    		// qName代表帶有前綴的限定名,當限定名不能獲取時是空串,如jd:tagname;
    		// attributes代表該標籤的所有屬性,可以對其各個屬性進行分析
    public void endElement (String uri, String localName, String qName);
    public void characters (char[] ch, int start, int length);   // 標籤內容事件的觸發,從ch的start位置的length長度的字符是有效的,其他位置的字符看做是無效的。
    		// 如果一個標籤的內容過長,可能內容會分段連續多次觸發該事件,可以用一個全局變量記錄內容,每一次把新的有效內容append到變量中,最後在結束標籤中處理該變量,處理完將該變量設置爲空字符串。
    public void processingInstruction (String target, String data); // 處理形如<?xml-stylesheet type="type" href="uri" ?>的處理指令
    public void startPrefixMapping (String prefix, String uri); // 接收命名空間映射的開始的通知
    public void endPrefixMapping (String prefix); // 接收命名空間映射的結束的通知
    
    // ErrorHandler
    public void warning (SAXParseException e); // 警示那些不是由 XML 規範定義的錯誤,但是它可能值得注意
    public void error (SAXParseException e); //  警示那些由 XML 規範定義的錯誤
    public void fatalError (SAXParseException e); // 警示那些由 XML 規範定義的致命錯誤
    
    // DTDHandler
    public void notationDecl (String name, String publicId, String systemId); // 通知應用程序已經聲明瞭一個標記
    public abstract void unparsedEntityDecl (String name, String publicId, String systemId, String notationName); // 通知應用程序已經發現了一個未經過語法分析的實體聲明

	// EntityResolver
    InputSource	resolveEntity(String publicId, String systemId);
}

  SAX2解析是SAX的新版本,它首先需要通過解析器工廠XMLReaderFactory的靜態方法createXMLReader創建一個XMLReader實例,然後設置XMLReader需要處理的各種處理器,最後調用XMLReader實例的parse方法。

final public class XMLReaderFactory {
   public static XMLReader createXMLReader ();
}

public interface XMLReader {
   public void parse (InputSource input); // 根據本對象的配置開始解析文件
   
   // 以下設置各類處理器,也可以把各類處理器都設置爲同一個DefaultHandler的子類
   public void setDTDHandler (DTDHandler handler); 
   public DTDHandler getDTDHandler ();
   public void setContentHandler (ContentHandler handler);
   public ContentHandler getContentHandler ();
   public void setErrorHandler (ErrorHandler handler);
   public ErrorHandler getErrorHandler ();
   public void setEntityResolver(EntityResolver resolver);
   
   // 設置參數控制解析器的工作細節,各種控制對應的name爲什麼,自行百度
   public boolean getFeature (String name);
   public void setFeature (String name, boolean value);
   public Object getProperty (String name);
   public void setProperty (String name, Object value);
}

  SAX也可以生成XML文件,首先通過(SAXTransformerFactory)SAXTransformerFactory.newInstance()得到一個SAXTransformerFactory工廠實例,然後通過工廠的newTransformerHandler()方法獲取一個處理器TransformerHandler,通過處理器setResult來設置保存位置,還可以通過處理器的getTransformer來獲取轉換器並對轉換器進行一些配置(如設置輸出編碼格式等),最後調用處理器的函數進行處理(從startDocument函數開始,到endDocument函數結束,中間可以通過startElement與endElement來創建節點等操作)。

StAX(Streaming API for XML)解析方式

  StAX是Java6之後內置的XML解析模型,與SAX類似是基於流模型的,但SAX是基於推模式,而StAX是基於拉模式的。StAX解析有兩組框架,分別爲基於指針和基於迭代器的方式。

// 事件類型定義
public interface XMLStreamConstants {
	public static final int START_ELEMENT=1; // 指示事件是一個開始元素
	public static final int END_ELEMENT=2; // 指示事件是一個結束元素
	public static final int PROCESSING_INSTRUCTION=3; // 指示事件是一條處理指令
	public static final int CHARACTERS=4; // 指示事件是一些字符
	public static final int COMMENT=5; // 指示事件是一個註釋
	public static final int SPACE=6; // 字符是空格
	public static final int START_DOCUMENT=7; // 指示事件是一個開始文檔
	public static final int END_DOCUMENT=8; // 指示事件是一個結束文檔
	public static final int ENTITY_REFERENCE=9; // 指示事件是一個實體引用
	public static final int ATTRIBUTE=10; // 指示事件是一個屬性
	public static final int DTD=11; // 指示事件是一個 DTD
	public static final int CDATA=12; // 指示事件是一個 CDATA 節
	public static final int NAMESPACE=13; // 指示事件是一個名稱空間聲明
	public static final int NOTATION_DECLARATION=14; // 指示一個 Notation
	public static final int ENTITY_DECLARATION=15; // 指示一個 Entity Declaration
}

  基於指針的方式首先通過XMLInputFactory的靜態方法newFactory或newInstance獲取一個XMLInputFactory對象,然後通過XMLInputFactory的createXMLStreamReader方法創建一個XMLStreamReader對象,最後對XMLStreamReader對象進行解析(該對象的大部分方法就是相對於當前事件的解析,其next方法將當前事件設置爲下一個事件)。
  基於指針的方式寫入xml文件,首先通過XMLOutputFactory的靜態方法newFactory或newInstance獲取一個XMLOutputFactory對象,在通過XMLOutputFactory對象createXMLEventWriter方法創建一個XMLStreamWriter對象,然後可以對XMLStreamWriter對象做基本的設置,最後寫入各種事件。

public abstract class XMLInputFactory {
	public static XMLInputFactory newInstance/newFactory();
	public abstract XMLStreamReader createXMLStreamReader(Reader reader / Source source / InputStream stream); // 創建一個基於指針的reader
 	public abstract XMLStreamReader createXMLStreamReader(InputStream stream, String encoding);
 	public abstract XMLEventReader createXMLEventReader(Reader reader / Source source / InputStream stream / XMLStreamReader reader); // 創建一個基於迭代器的reader
 	public abstract XMLEventReader createXMLEventReader(InputStream stream, String encoding);

 	public abstract void setProperty(java.lang.String name, Object value);
 	public abstract Object getProperty(java.lang.String name);
 	public abstract boolean isPropertySupported(String name);
}

public interface XMLStreamReader extends XMLStreamConstants {
	public void close(); // 釋放與此 Reader 關聯的所有資源,此方法不會關閉底層輸入源。
	
	// 指針相關
	public boolean hasNext();
 	public int next(); // 當前指針指向下一個事件並返回事件類型,事件類型定義在XMLStreamConstants中
 	public int nextTag(); // 在到達 START_ELEMENT 或 END_ELEMENT 之前,跳過所有空格、COMMENT 或 PROCESSING_INSTRUCTION,期間出現其他事件會拋出 異常
 	
	public int getEventType(); // 返回事件類型
	public void require(int type, String namespaceURI, String localName); // 測試當前事件是否爲type事件,並且名字空間爲namespaceURI,標籤名爲localName,當namespaceURI和localName爲null時表示該位置不檢查
	public boolean isStartElement(); // 判斷事件是否爲StartElemnt事件
 	public boolean isEndElement();
 	public boolean isCharacters();
 	public boolean isWhiteSpace();

  	public boolean hasName(); // 爲START_ELEMENT 或 END_ELEMENT返回true,否則返回false
	public String getElementText(); // 讀取element的文本  
  
	public String getAttributeValue(String namespaceURI, String localName);
 	public int getAttributeCount();
 	public QName getAttributeName(int index);
 	public String getAttributeNamespace(int index);
 	public String getAttributeLocalName(int index);
 	public String getAttributePrefix(int index);
 	public String getAttributeType(int index);
 	public String getAttributeValue(int index);
 	public boolean isAttributeSpecified(int index);
  
	public int getNamespaceCount();
 	public String getNamespacePrefix(int index);
 	public String getNamespaceURI(int index);
 	public NamespaceContext getNamespaceContext();
 	public QName getName();
	public String getLocalName();
 	public String getNamespaceURI();
 	public String getPrefix();
  
	public boolean hasText();
 	public String getText();
	public char[] getTextCharacters();
	public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length);	
	public int getTextStart();
	public int getTextLength();  
 	
	public String getPITarget();
 	public String getPIData();
 	
	public String getEncoding(); // 讀取文件的reader的編碼
	public String getVersion();
	public boolean standaloneSet(); // 返回xml文件是否設置了standalone
	public boolean isStandalone(); // 返回設置的standalone的值
	public String getCharacterEncodingScheme(); // xml文件中設置的編碼
	
	public Object getProperty(java.lang.String name);
}

  基於迭代器的方式首先也是通過XMLInputFactory的靜態方法newFactory或newInstance獲取一個XMLInputFactory對象,然後通過XMLInputFactory的createXMLEventReader方法創建一個XMLEventReader對象,XMLEventReader對象就是一個迭代器,每一次調用XMLEventReader對象的nextEvent方法都會返回下一個XMLEvent對象,最後對XMLEvent進行解析。
  基於迭代器的方式保存xml文件,直接調用XMLEvent的writeAsEncodedUnicode將當前事件寫入writer。

public interface XMLEventReader extends Iterator {
 	public boolean hasNext();
 	public XMLEvent nextEvent(); // 獲取下一個事件
 	public XMLEvent nextTag(); // 到下一個START_ELEMENT 或 END_ELEMENT事件,中途遇到無意義的空白事件直接跳過,遇到其他事件會拋出異常
 	public XMLEvent peek();
 	public String getElementText(); // 讀取純文本元素的內容
 	public Object getProperty(String name);
	public void close(); // 釋放資源(不會釋放底層輸入源,需要手動釋放)
}

// 對於每一個類型的事件都有一個事件類實現了該接口,實際事件有:
// Attribute, Characters, Comment, DTD, EndDocument, EndElement, EntityDeclaration, EntityReference, 
// Namespace, NotationDeclaration, ProcessingInstruction, StartDocument, StartElement 
public interface XMLEvent extends javax.xml.stream.XMLStreamConstants {
	public int getEventType(); // 事件類型定義在XMLStreamConstants中
 	public boolean isStartElement(); // 若返回ture,此時的XMLEvent可以強轉爲StartElement 
	public boolean isAttribute();
	public boolean isNamespace();
	public boolean isEndElement();
	public boolean isEntityReference();
	public boolean isProcessingInstruction();
	public boolean isCharacters();
	public boolean isStartDocument();
	public boolean isEndDocument();
	
 	public StartElement asStartElement();
	public EndElement asEndElement();
	public Characters asCharacters();
	public QName getSchemaType();
    
	public void writeAsEncodedUnicode(Writer writer); // 寫出到流
}

JDOM(Java Document Object Model)解析方式

  JDOM也是基於對象的解析方式,它不是JRE內置的包,需要引入JDOM包到項目。在JDOM中主要分爲幾種類,分別放在幾個不同的包中:

  • org.jdom 包含最後解析出來的對象,主要包含了容器與節點
  • org.jdom.filter 包含對各種節點列表進行過濾的過濾器基類
  • org.jdom.input 構件JDOM的各類對象(主要是節點與容器),來源於XML文件或DOM對象,實現DOM對象轉JDOM對象以及解析XML得到Document
  • org.jdom.output 將JDOM的各類對象輸出,如保存到XML文件或轉換爲DOM對象
  • org.jdom.xpath 包含了對xml文檔xpath操作的類

  JDOM的樹型結構中,有兩個基類Content與Parent,Parent表示可以作爲容器,Content表示一個節點(節點必然掛在一個容器下),Parent子類有Document與Element,Content子類有Comment、Element、EntityRef、Text、ProcessingInstruction、CDATA、DocType。Document是容器不是節點,它下面可以掛載節點但自身並不掛載到節點下,Element既是容器又是節點,它下面可以掛載節點,也可以掛載到其他容器下
  解析XML首先new一個SAXBuilder 對象,然後調用該SAXBuilder對象的build方法創建一個org.jdom2.Document,最後對該Document進行分析。

public class SAXBuilder implements SAXEngine {
    public SAXBuilder();
    public Document build(InputStream in / File file / URL url / InputSource in / Reader characterStream));

    public void setFeature(String name, boolean value);
    public void setProperty(String name, Object value);
        
    public SAXHandlerFactory getSAXHandlerFactory() ;
    public void setSAXHandlerFactory(SAXHandlerFactory factory);
    public ErrorHandler getErrorHandler();
    public void setErrorHandler(ErrorHandler errorHandler);
    public DTDHandler getDTDHandler();
    public void setDTDHandler(DTDHandler dtdHandler);
}

// 代表一個節點,只有這樣的節點可以作爲子節點
public abstract class Content {
	public final CType getCType();  // 節點類型
	public Content detach(); // 從父節點中去掉該節點
	public Parent getParent(); 
	final public Element getParentElement(); // 如果有父節點且是父節點是Element類型,那麼返回父節點,否則返回null
	protected Content setParent(Parent parent); // 設置父節點
	public Document getDocument(); // 獲取跟容器
	public abstract String getValue();
	public Content clone(); // 深克隆節點並將克隆的節點detach()
}

// 代表一個容器,只有容器可以作爲父節點
public interface Parent extends Cloneable, NamespaceAware, Serializable {
	Document getDocument();
	Parent getParent();
	
	List<Content> cloneContent(); // 深克隆所有子節點並將克隆的子節點detach()
	Object clone(); // 深克隆當前節點及其子節點
	
	int getContentSize(); // 子節點個數
	int indexOf(Content child); // 某個節點是其第幾個子節點或不是其子節點時返回-1
	Content getContent(int index); // 獲取子節點
	List<Content> getContent(); // 獲取所有子節點
	<E extends Content> List<E> getContent(Filter<E> filter); // 獲取過濾後的子節點
	IteratorIterable<Content> getDescendants(); // 獲取所有後代節點
	<E extends Content> IteratorIterable<E> getDescendants(Filter<E> filter); // 獲取過濾後的後代節點
	
	List<Content> removeContent(); // 移除所有子節點
	<E extends Content> List<E> removeContent(Filter<E> filter); // 移除符合條件的子節點
	boolean removeContent(Content child);
	Content removeContent(int index);
	
	public Parent addContent(Content child);
	public Parent addContent(Collection<? extends Content> c);
	public Parent addContent(int index, Content child);
	public Parent addContent(int index, Collection<? extends Content> c);
	void canContainContent(Content content, int index, boolean replace); // 檢查content是否能在index位置插入或替換,不能發生異常
}

public class Document extends CloneBase implements Parent {
    public Document();
    public Document(Element rootElement, DocType docType, String baseURI);
    public Document(Element rootElement, DocType docType);
    public Document(Element rootElement);
    public Document(List<? extends Content> content);
   
    public void setProperty(String id, Object value); // 爲當前document添加一些額外屬性,可供分析時使用
    public Object getProperty(String id);
   
    public boolean hasRootElement();
    public Element detachRootElement();
    public Element getRootElement();
    public Document setRootElement(Element rootElement);
    public DocType getDocType();
    public Document setDocType(DocType docType);
}

public class Element extends Content implements Parent {
	public Element(final String name, final Namespace namespace);
	public Element(final String name);
	public Element(final String name, final String uri);
	public Element(final String name, final String prefix, final String uri);
	
	public boolean isAncestor(final Element element); // 是不是某個element的祖先
	public boolean isRootElement();
		
	public String getName(); // 返回不帶前綴標籤名
	public String getQualifiedName(); // 返回帶前綴的標籤名
	public Element setName(final String name);
	public Namespace getNamespace();
	public Element setNamespace(Namespace namespace);
	public String getNamespacePrefix();
	public String getNamespaceURI();
	
	public String getText();
	public String getTextTrim();
	public String getTextNormalize();
	public String getChildText(final String cname); // getChild(cname).getText()
	public String getChildTextTrim(final String cname);
	public String getChildTextNormalize(final String cname);
	public String getChildText(final String cname, final Namespace ns);
	public String getChildTextTrim(final String cname, final Namespace ns);
	public String getChildTextNormalize(final String cname, final Namespace ns);
	public Element setText(final String text);
	
	public boolean hasAttributes();
	AttributeList getAttributeList();
	public List<Attribute> getAttributes();
	public Attribute getAttribute(final String attname);
	public Attribute getAttribute(final String attname, final Namespace ns);
	public String getAttributeValue(final String attname);
	public String getAttributeValue(final String attname, final String def);
	public String getAttributeValue(final String attname, final Namespace ns);
	public String getAttributeValue(final String attname, final Namespace ns, final String def);
	public Element setAttributes(final Collection<? extends Attribute> newAttributes);
	public Element setAttribute(final String name, final String value);
	public Element setAttribute(final String name, final String value, final Namespace ns);
	public boolean removeAttribute(final String attname);
	public boolean removeAttribute(final String attname, final Namespace ns);
	public boolean removeAttribute(final Attribute attribute);
	
	// 與getContext相關方法的返回子節點,與getChild相關的方法返回子element節點
	public List<Element> getChildren();
	public List<Element> getChildren(final String cname);
	public List<Element> getChildren(final String cname, final Namespace ns);
	public Element getChild(final String cname, final Namespace ns);
	public Element getChild(final String cname);
	public boolean removeChild(final String cname);
	public boolean removeChild(final String cname, final Namespace ns);
	public boolean removeChildren(final String cname);
	public boolean removeChildren(final String cname, final Namespace ns);
}

  JDOM也支持將Doument保存到xml文件,首先創建一個輸出對象XMLOutputter,然後調用其output(Doument document, Writer writer / OutputStream os)方法寫入文件。
  JDOM也支持XPath操作,早期XPath可以直接調用其靜態方法selectNodes與selectSingleNode解析,簡單方便,但現已被廢棄這種操作方式。代替方式需要引入jaxen包,通過XPathFactory的靜態方法instance()得到一個XPathFactory對象,並根據XPathFactory對象的compile方法獲取一個XPathExpression對象,通過XPathExpression對象的evaluate方法來進行解析。

public abstract class XPathFactory {
	public static final XPathFactory instance();
	public XPathExpression<Object> compile(String expression);
}

public interface XPathExpression<T> extends Cloneable {
	public List<T> evaluate(Object context);
	public T evaluateFirst(Object context); // 取evaluate的第一個
}

DOM4J(Document Object Model for Java)解析方式

  DOM4J與其他幾種解析方式相比,其性能最好,既支持對象模型又支持事件模型,故得到了廣泛的應用,在進行XML解析,若無特殊需求,技術選型就選DOM4J
  DOM4J獲取Document對象的方式主要有以下幾種,第一種是從XML文件解析得到,先new一個SAXReader對象,然後調用其read方法解析得到Document對象;第二種是解析xml格式的String,直接調用DocumentHelper的靜態方法parseText獲取Document對象;第三種是直接調用DocumentHelper的靜態方法createDocument創建一個空的Document對象;還有一種是從DOM的Document轉換爲DOM4J的Document,通過new一個DOMReader對象,然後調用其read方法得到,其具體操作與SAXReader相似。
  生成xml文件,只需要調用document的write(Writer writer)方法即可(期數write方法是node定義的,也就是說可以只寫入某個節點到writer)。

// 從XML文件創建Document對象
public class SAXReader {
    public SAXReader();    
    public Document read(File file / URL url / InputStream in / Reader reader / InputSource in);        
    
    public ErrorHandler getErrorHandler();
    public void setErrorHandler(ErrorHandler errorHandler);   
    public EntityResolver getEntityResolver();
    public void setEntityResolver(EntityResolver entityResolver);
    
    // ElementHandler可以在解析xml時對Element做額外的操作,不會影響生成Element的過程,但可以對生成的Element進行修改
    public void setDefaultHandler(ElementHandler handler); // 所有element節點都會調用handler處理
    public void addHandler(String path, ElementHandler handler); // path路徑(絕對路徑如/root/parent/node)的elemnt會進行handler處理,	
    			// 被處理的路徑及其下面路徑(如/root/parent/node/child)的element都不會再調用默認的ElementHandler了,
    			// 該函數雖然是add方法,但相同的路徑,後面的會覆蓋前面的handler
    public void removeHandler(String path);
    public void resetHandlers();
    
    public void setProperty(String name, Object value);
    public void setFeature(String name, boolean value);
        
	... 另外有一系列函數,用來爲創建的解析器的解析細節設置控制參數
}

public interface ElementHandler {
    void onStart(ElementPath elementPath); // 可以獲取Attribute但內容與子Element這裏都無法獲取,需要到end中獲取
    void onEnd(ElementPath elementPath);
}

public interface ElementPath {
    int size(); // 路徑級數如/a/b/c爲3
    Element getElement(int depth); // 獲取第n級元素,如depth爲0返回根元素
    String getPath();
    Element getCurrent(); // 當前element
    void addHandler(String path, ElementHandler handler); // path可以再用相對當前路徑的相對路徑如c/e
    void removeHandler(String path);
}

// 幫助創建DOM4J的對象,以及XPath的簡化操作
public final class DocumentHelper {
	// 創建DOM4J相應的對象,都不需用new,都用該類的靜態方法創建
    public static Document parseText(String text); // 從xml文本字符串解析得到Document
    public static Document createDocument(); // 創建一個空的Document對象
    public static Document createDocument(Element rootElement); // 創建一個包含根元素的Document
    public static Element createElement(QName qname);
    public static Element createElement(String name);
    public static Element makeElement(Branch source, String path);
    public static Attribute createAttribute(Element owner, QName qname, String value);
    public static Attribute createAttribute(Element owner, String name, String value);
    public static CDATA createCDATA(String text);
    public static Comment createComment(String text);
    public static Text createText(String text);
    public static Entity createEntity(String name, String text);
    public static Namespace createNamespace(String prefix, String uri);
    public static ProcessingInstruction createProcessingInstruction(String pi, String d);
    public static ProcessingInstruction createProcessingInstruction(String pi, Map data);
    public static QName createQName(String localName, Namespace namespace);
    public static QName createQName(String localName);

	// 對應XPath的方法
    public static XPath createXPath(String xpathExpression);
    public static XPath createXPath(String xpathExpression, VariableContext context);
    public static NodeFilter createXPathFilter(String xpathFilterExpression);
    public static Pattern createPattern(String xpathPattern);
    public static List selectNodes(String xpathFilterExpression, List nodes);
    public static List selectNodes(String xpathFilterExpression, Node node);
    public static void sort(List list, String xpathExpression);
    public static void sort(List list, String expression, boolean distinct);
}

  在DOM中,Node表示的節點可以掛載到其他節點上,也可以掛載其他節點,而在JDOM中,節點Content表示可以掛載在容器下面,而容器Parent表示可以掛載節點,而Parent不是Content的子類,在DOM4J中,節點Node表示普通節點,而容器Branch是Node的子類,這更符合實際模型。
  Node直接子類有Attribute、DocumentType、Entity、ProcessingInstruction,另外還有直接子類Branch與CharacterData。Branch表示節點可以作爲容器,其子類有Document與Element。CharacterData有子類CDATA、Comment與Text

public interface Node extends Cloneable {
    String asXML(); // 將節點轉換爲xml字符串
    void write(Writer writer); // 將節點xml格式寫入流
    void accept(Visitor visitor); // 當前Node以及其子節點都會根據其節點類型調用Vistor相應的visit方法
    Object clone(); // 深克隆
        
    // 節點基本信息    
    short getNodeType();
    String getNodeTypeName();
    String getName();
    void setName(String name);
    String getText();
    void setText(String text);
    
	// XPath相關
	XPath createXPath(String xpathExpression);
    String getPath(); // 絕對xpath
    String getPath(Element element); // 相對xpath
    String getUniquePath();
    String getUniquePath(Element element);
    boolean matches(String xpathExpression);
    
    // 父子節點相關
    Element getParent();
    void setParent(Element element);
    Document getDocument();
    void setDocument(Document document);    
    boolean hasContent(); // content表示子節點
    Node detach(); // 解除綁定,從父容器中移除
    List selectNodes(String xpathExpression);
    List selectNodes(String xpathExpression, String comparisonXPathExpression);
    List selectNodes(String xpathExpression, String comparisonXPathExpression, boolean removeDuplicates);
    Node selectSingleNode(String xpathExpression);    
}

public interface Branch extends Node {
    Node node(int var1); // 第i個子節點
    int indexOf(Node var1);
    int nodeCount();
    List content();
    void setContent(List contents);
    Iterator nodeIterator();
    void appendContent(Branch branch);
    void clearContent();
    List processingInstructions();
    List processingInstructions(String target);
    ProcessingInstruction processingInstruction(String target);
    void setProcessingInstructions(List listOfPIs);
    Element addElement(String name / QName qName); // 新建一個名爲name的element
    Element addElement(String qualifiedName, String namespaceURI);
    void add(Node node / Comment comment / Element element / ProcessingInstruction pi);
    boolean remove(Node node / Comment comment / Element element / ProcessingInstruction pi / String target);
}

public interface Document extends Branch {
    Element getRootElement();
    void setRootElement(Element rootElement);
    Document addComment(String comment);
    Document addProcessingInstruction(String target, String text);
    Document addProcessingInstruction(String taget, Map data);
    Document addDocType(String name, String publicId, String systemId);
    DocumentType getDocType();
    void setDocType(DocumentType docType);
    EntityResolver getEntityResolver();
    void setEntityResolver(EntityResolver resolver);
    String getXMLEncoding();
    void setXMLEncoding(String encoding);
}

public interface Element extends Branch {
    boolean isRootElement();
    boolean hasMixedContent(); // 是否既有子element又有文本
    boolean isTextOnly();
    Element createCopy(); // 深克隆並detach
    Element createCopy(String name / QName qName); // 深克隆並detach,並改名
    
    QName getQName();
    QName getQName(String qualifiedName);
    String getQualifiedName();
    void setQName(QName qualifiedName);
    String getText();
    String getTextTrim();
    Object getData();
    void setData(Object var1);
    
    // 元素定義名字空間的prefix不會重複,但uri可能重複,也就是說多個prefix可以對應同一個uri,但不可以多個uri對應同一個prefix
    Namespace getNamespace(); // 當前QName的prefix對應的那一個名字空間
    Namespace getNamespaceForPrefix(String prefix); 
    List getNamespacesForURI(String uri); 
    Namespace getNamespaceForURI(String uri); // 如果不止一個,返回的是隨機的
    String getNamespacePrefix();
    String getNamespaceURI();
    List additionalNamespaces();
    List declaredNamespaces();
    
    Element addAttribute(String name, String value);
    Element addAttribute(QName qName, String value);
    Element addComment(String comment);
    Element addCDATA(String cdata);
    Element addEntity(String name, String text);
    Element addNamespace(String prefix, String uri);
    Element addProcessingInstruction(String taget, String text);
    Element addProcessingInstruction(String taget, Map data);
    Element addText(String text);
    void add(Attribute attribute / CDATA cdata / Entity entity / Text text / Namespace ns);
    boolean remove(Attribute attribute / CDATA cdata / Entity entity / Text text / Namespace ns);
    
    List attributes();
    void setAttributes(List attrs);
    void appendAttributes(Element element); // 將element的所有attribute添加到當前element
    int attributeCount();
    Iterator attributeIterator();
    Attribute attribute(int index / String name / QName qName);
    String attributeValue(String name / QName qName);
    String attributeValue(String name / QName qName, String defaultValue);
    
    List elements();
    List elements(String name / QName qName);
    Iterator elementIterator();
    Iterator elementIterator(String name / QName qName);
    Element element(String name / QName qName);
    String elementText(String name / QName qName);
    String elementTextTrim(String name / QName qName);   
}

  DOM4J對XPath支持同JDOM一樣需要引入jaxen包。首先通過DocumentHelper的靜態方法createXPath創建XPath對象,然後通過XPath對象的e,但Node節點已經有根據XPath表達式過濾的方法,所以XPath類在DOM4J中很少用。

public interface XPath extends NodeFilter {
    String getText(); // 返回XPath表達式
    boolean matches(Node node); // 節點是否匹配該XPath表達式
    Object evaluate(Object context); // 根據上下文解析表達式,取代了Object selectObject(Object context)方法,返回值可能是List、Node、String、Number、boolean,後面的方法大多根據該方法強轉得到
    List selectNodes(Object context); // 獲取節點列表
    List selectNodes(Object context, XPath sortXPath); // 同sortXPath.sort(selectNodes(context));
    List selectNodes(Object context, XPath sortXPath, boolean distinct); // 同sortXPath.sort(selectNodes(context), distinct);
    Node selectSingleNode(Object context); // 獲取selectNodes的第一個Node
    String valueOf(Object context); // 解析獲取String
    Number numberValueOf(Object context); // 解析獲取Number值
    boolean booleanValueOf(Object context); // 解析獲取boolean值
    void sort(List list, boolean distinct); // 當作爲sortXPath的時候需要實現該方法,distinct爲是否去重
    void sort(List list); // sort(list, true)
}

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