首頁>技術>

parsing包背景知識

XML檔案

可擴充套件標記語言(eXtensible Markup Language,XML)是一種標記語言。所謂的標記是指計算機所能理解的資訊符號,透過標記可以實現軟體開發者與計算機之間的資訊溝通。常見的 HTML便是一種標記語言,不過 HTML語言中的標籤(如“<h1> </h1>”“<img\>”等)都是固定的,是不可擴充套件的。XML則可以由開發人員自由擴充套件定義。

XML可擴充套件的一個重要表現就是 XML文件的結構是可以自由定義的。定義 XML文件可以使用 DTD(Document Type Definition,文件型別定義),也可以使用 XML Schema。不過在介紹 DTD和 XML Schema之前,我們先來了解 XML文件的結構。

XML文件中包含眾多的節點。節點分為以下幾類:元素節點、屬性節點、文字節點、文件節點等。在實際指代中,可以省略“節點”二字,也可以將以上各類統稱為“節點”。

以程式碼11-1給出的 XML文件為例。文件第一行為 XML宣告,它聲明瞭 XML的版本是 1.0,使用的編碼是 UTF-8。XML中從一個標籤開始(含)到一個標籤結束(含)的部分叫作元素節點,例如,從第一個“<user>”到第一個“</user>”之間的部分就是一個 user元素節點。元素節點可以有屬性節點,如“type="student"”。元素節點可以包含其他元素節點,例如,user元素包含了 id、name、school這三個元素節點。元素節點中也可以有文字節點,例如,第一個 name元素節點中就包含了文字節點,值為“易哥”。

【程式碼11-1】

程式碼11-1中,members元素位於頂層,因此是根元素。每一個 XML文件都必須有一個根元素。

XML文件實際上表述了一棵樹。圖11-1就展示了程式碼11-1的結構樹。

圖11-1 結構樹

在一個 XML文件中,可以存在什麼元素及每個元素是怎樣的,這些是由 XML文件的定義檔案來進行描述的,如 DTD(此類檔案的字尾名為dtd)或者 XML Schema(此類檔案的字尾名為 xsd)。

以 XML Schema文件為例,我們可以使用程式碼11-2來定義程式碼11-1中展示的 XML片段。

【程式碼11-2】

而使用 DTD,則可以使用程式碼11-3來定義程式碼11-1中展示的 XML片段。

【程式碼11-3】

程式碼11-3所示的 DOCTYPE宣告中,members是根節點名稱,“[]”中為節點的限制條件。而且,DTD也支援使用外部 DTD文件來定義 XML文件,例如,在 MyBatis的配置文件開頭可以看到如程式碼11-4所示的片段就引用了外部的 DTD文件。

【程式碼11-4】

在程式碼11-4所示的 DOCTYPE宣告中,各個專案的含義如下。

· configuration:表示當前 XML文件的根節點為 configuration。

· PUBLIC:表示當前 XML文件採用的是公共的 DTD。

·-//mybatis.org//DTD Config 3.0//EN:表示 DTD文件的資訊。

--:表示是非 ISO組織;

-mybatis.org:表示組織名稱 mybatis.org;

-DTD Config 3.0:表示文字描述,包括版本號;-EN:表示 DTD文件是英文。

· http://mybatis.org/dtd/mybatis-3-config.dtd:表示文件的下載地址。

XPath

在上一節我們已經說明了 XML表述一種樹狀結構,並透過圖11-1給出了程式碼11-1中XML片段的結構樹。而 XPath(XML Path Language,XML路徑語言)作為一種小型的查詢語言能夠根據 XML結構樹在樹中尋找節點。

XPath定義了一組語法,能夠從結構樹中篩選出滿足要求的節點。

如果讀者對 CSS選擇器或 jQuery選擇器比較熟悉的話,那掌握 XPath的語法還是非常簡單的,因為這些選擇器的語法思路是相通的。

我們可以透過表11-1中所示的例子來簡單瞭解 XPath的語法。

表11-1 XPath語法示例

javax.xml.xpath包提供了強大的 XPath解析功能,可以基於它實現 XML的解析。例如,可以透過程式碼11-5解析程式碼11-1給出的 XML片段,得到圖11-2所示的程式執行結果。

【程式碼11-5】

圖11-2 程式執行結果

在程式碼11-5中,透過“/members/user[id=1]”定位出了一個 user元素,該元素滿足以下條件。

· 該元素是根元素 members的直接子元素;

· 該元素含有 id子元素,且 id子元素值為 1。

XML解析

MyBatis的配置檔案與對映檔案均是 XML檔案,因此解析並讀取 XML文件中的內容是 MyBatis展開後續工作的基礎。

MyBatis中的 parsing包就是用來進行 XML檔案解析的包。在解析XML檔案的過程中,XPathParser類與 XNode類是兩個最為關鍵的類,圖11-3給出了這兩個類主要關係的類圖。

圖11-3 XPathParser類與XNode類主要關係的類圖

透過圖11-3 可以看出,XPathParser 類中封裝了“javax.xml.xpath.XPath”類的物件。而透過 11.1.2節的介紹我們也知道 XPath物件是 XML解析的利器,因此 XPathParser類便具有了 XML解析的能力。

程式碼11-6給出了 XPathParser類的帶註釋的屬性。

【程式碼11-6】

有必要說明一下,上述“private Properties variables”屬性儲存的內容就是 MyBatis配置檔案中 properties 節點的資訊。properties 節點會在解析配置檔案的最開始就被解析,然後相關資訊會被放入“private Properties variables”屬性並在解析後續節點時發揮作用,在11.3節我們會詳細介紹這一點。

XPathParser存在多個過載的構造方法,它們均根據傳入的引數完成屬性的初始化並構造出 XML文件對應的 Document物件。除去構造方法外,便是大量提供 XML文件中節點解析功能的“eval*”方法,這些方法最後都呼叫瞭如程式碼11-7所示的 evaluate方法。

【程式碼11-7】

在 evaluate方法中,使用“javax.xml.xpath.XPath”物件進行了節點解析。因此,整個XPathParser類本質就是對“javax.xml.xpath.XPath”的封裝和呼叫,可以把 XPathParser類看作 javax.xml.xpath.XPath類的包裝類。

同樣,可以將 parsing 包中的 XNode 類看作 org.w3c.dom.Node類的包裝類。org.w3c.dom.Node類是用來表示 DOM中節點的類,而 XNode類只是在 org.w3c.dom.Node類的基礎上提取和補充了幾個屬性。程式碼11-8給出了 XNode物件的屬性。

【程式碼11-8】

XNode物件的上述屬性中,name、body、attributes這三個屬性是從“org.w3c.dom.Node”物件中提取出來的,而 variables、xpathParser這兩個屬性則是額外補充進來的。

我們知道 XPathParser類具有解析 XML節點的能力,也就是說,XNode類中封裝了自身的解析器。在一個類中封裝自己的解析器,這是一種非常常見的做法,如此一來這個類不需要外界的幫助便可以解析自身,即獲得了自解析能力。

大家可能有過這樣的經歷:由於新安裝的計算機上沒有解壓軟體,於是從網路或者朋友那裡得到了一份解壓軟體。可是,拿到手的解壓軟體安裝包卻是一個壓縮檔案。尚未安裝解壓軟體的你必然沒法開啟壓縮檔案獲得安裝包。而自解壓檔案(SelF-eXtracting,SFX)能夠幫助你擺脫這個困境。自解析類也有類似的優點,它減少了對外部類的依賴,具有更高的內聚性,也更為易用。

正是得益於 XNode類的自解析特性,它本身提供了一些“eval*”方法,從而能夠解析自身節點內的資訊。

文件解析中的變數替換

我們在 11.2節曾提及,MyBatis配置檔案中 properties節點會在解析配置檔案的最開始就被解析,並在解析後續節點時發揮作用。可是如何才能讓這些資訊在 XML 檔案的解析中發揮作用呢?

回到 XPathParser類,其“evalString(Object,String)”方法如程式碼11-9所示。

【程式碼11-9】

我們發現在解析字串時,透過“PropertyParser.parse”方法對解析出來的結果進行了進一步處理。而這一步處理中,properties節點的資訊便發揮了作用。

PropertyParser類是屬性解析器,與之關係密切的幾個類的類圖如圖11-4所示。

圖11-4 PropertyParser及其相關類的類圖

下面以 GenericTokenParser類為入口閱讀圖11-4所示的幾個類。GenericTokenParser類是通用的佔位符解析器,共有三個屬性,相關注釋如程式碼11-10所示。

【程式碼11-10】

GenericTokenParser 類中有唯一的一個 parse 方法,該方法主要完成佔位符的定位工作,然後把佔位符的替換工作交給與其關聯的 TokenHandler 處理。我們透過一個例子對parse方法的功能進行介紹。假設“openToken=#{”“closeToken=}”,向 GenericTokenParser中的 parse方法傳入的引數為“jdbc:mysql://127.0.0.1:3306/${dbname}?serverTimezone=UTC”,則 parse方法會將被“#{”和“}”包圍的 dbname 字串解析出來,作為輸入引數傳入 handler 中的handleToken方法,然後用 handleToken方法的返回值替換“${dbname}”字串。

GenericTokenParser提供的佔位符定位功能應用非常廣泛,而不僅僅侷限在 XML解析中,畢竟它的名稱是“通用的”佔位符解析器。SQL語句的解析也離不開它的幫助。SQL語句中使用“#{}”或“${}”來設定的佔位符也是依靠 GenericTokenParser 來完成解析的,流程與本節介紹的一樣。

TokenHandler 是一個介面,如程式碼11-11 所示,它只定義了一個抽象方法handleToken。handleToken 方法要求輸入一個字串,然後返回一個字串。例如,可以輸入一個變數的名稱,然後返回該變數的值。

【程式碼11-11】

PropertyParser類的內部類 VariableTokenHandler便繼承了該介面。程式碼11-12展示了VariableTokenHandler類的屬性。

【程式碼11-12】

瞭解了 VariableTokenHandler類的屬性後,再閱讀其 handleToken方法,如程式碼11-13所示。向 handleToken 方法中傳入輸入引數後,該方法會以輸入引數為鍵嘗試從 variables屬性中尋找對應的值返回。在這個由鍵尋值的過程中還可以支援預設值。

【程式碼11-13】

最後再看 PropertyParser中的靜態方法 parse,如程式碼11-14所示。它做了以下幾個工作將 GenericTokenParser提供的佔位符定位功能和 TokenHandler提供的字串替換功能串接在了一起。

· 建立一個 VariableTokenHandler物件(TokenHandler介面子類的物件)。該物件能夠從一個 Properties物件(這裡傳入的是 properties節點資訊)中根據鍵索引一個值。

· 建立了一個屬性解析器。只要設定了該屬性解析器要匹配的模式,它就能將指定模式的屬性值定位出來,然後將其替換為 TokenHandler介面中 handleToken方法的返回值。

【程式碼11-14】

這樣一來,只要在 XML檔案中使用“${”和“}”包圍一個變數名,則該變數名就會被替換成 properties節點中對應的值。

15
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 我看愣了,MySQL還能實現分散式鎖?