通过 .NET Framework 中的 XPath 和 XSLT API 方便地操作 XML 数据
本文假設(shè)您熟悉 Visual Basic .NET
下載本文的代碼: XPathandXSLT.exe (166KB)
摘要
XPath 是一種正在興起的通用查詢語(yǔ)言。通過(guò) XPath,可以在基于 XML 的數(shù)據(jù)源中識(shí)別和處理一組相關(guān)的節(jié)點(diǎn)。XPath 提供了一個(gè)基礎(chǔ)結(jié)構(gòu),它是 .NET Framework 中的 XML 支持的組成部分。XPath 導(dǎo)航模型甚至用在 XSLT 處理程序的內(nèi)部。在本文中,作者考察了 XPath 導(dǎo)航器和 XSLT 處理程序的實(shí)現(xiàn)細(xì)節(jié),并且包含了一些實(shí)際的示例,例如異步轉(zhuǎn)換、排序節(jié)點(diǎn)集和 ASP.NET 服務(wù)器端轉(zhuǎn)換。
XML 的主要優(yōu)點(diǎn)之一是,它使您可以用標(biāo)記和屬性標(biāo)記文本的某些部分。您在文本內(nèi)部標(biāo)記數(shù)據(jù)的原因是您計(jì)劃以后檢索它。那么,如何完成這一工作呢?您可以使用 XPath。
雖 然 XPath 不具有基于 XML 的語(yǔ)法,但它是為了以簡(jiǎn)潔的、相對(duì)簡(jiǎn)單的方式對(duì) XML 文檔的某些部分進(jìn)行尋址而定義的語(yǔ)言。更為重要的是,XPath 定義了一種常見(jiàn)的語(yǔ)法,以便您可以從實(shí)現(xiàn)了 XML 文檔對(duì)象模型 (DOM) 的類(lèi)內(nèi)部以及從 XSLT 中檢索節(jié)點(diǎn)。
在 Microsoft?.NET Framework 中,通過(guò)在 System.Xml.XPath 命名空間中定義的類(lèi)對(duì) XPath 查詢語(yǔ)言提供了完整的支持。.NET Framework 對(duì) XPath 的實(shí)現(xiàn)基于語(yǔ)言分析程序和評(píng)估引擎。XPath 查詢的整體體系結(jié)構(gòu)與數(shù)據(jù)庫(kù)查詢類(lèi)似。就像 SQL 命令一樣,需要準(zhǔn)備 XPath 表達(dá)式并將它們提交給運(yùn)行庫(kù)引擎以進(jìn)行評(píng)估。查詢是針對(duì) XML 數(shù)據(jù)源加以分析和執(zhí)行的。接下來(lái),您將取回一些表示查詢的結(jié)果集的信息。XPath 表達(dá)式可以返回節(jié)點(diǎn)集(即,有序的節(jié)點(diǎn)集合)、布爾值、數(shù)字或字符串。
在本文中,我將說(shuō)明 XPath 如何與 .NET Framework 中的 XmlDocument 類(lèi)和 XSLT 集成。我還將對(duì) XPathNavigator 類(lèi)(.NET Framework 使用它來(lái)遍歷 XML 文檔)進(jìn)行深入的剖析。
XPath 表達(dá)式
XPath 是一種專(zhuān)門(mén)設(shè)計(jì)的查詢語(yǔ)言,用于對(duì) XML 文檔的元素和文本進(jìn)行尋址。XPath 表示法在本質(zhì)上是聲明性的。任何有效的表達(dá)式都會(huì)使用強(qiáng)調(diào)節(jié)點(diǎn)之間層次關(guān)系的表示法來(lái)聲明節(jié)點(diǎn)模式。與文件系統(tǒng)路徑類(lèi)似,XPath 表達(dá)式從根(XPath 術(shù)語(yǔ)中的軸)前進(jìn)至源文檔中的特定節(jié)點(diǎn)集或值。然而,它與文件系統(tǒng)的相似性并不僅限于此。XPath 表達(dá)式總是在節(jié)點(diǎn)的上下文中求值。上下文節(jié)點(diǎn)由應(yīng)用程序指定,并且代表查詢的起點(diǎn)。它與當(dāng)前目錄的概念沒(méi)有太大區(qū)別。
XPath 查詢的上下文包括但不限于上下文節(jié)點(diǎn)和上下文節(jié)點(diǎn)集。上下文節(jié)點(diǎn)集是查詢所處理的節(jié)點(diǎn)的整個(gè)集合。通常,它是實(shí)際返回到應(yīng)用程序的節(jié)點(diǎn)集的超集。 XPath 上下文還包含位置和命名空間信息、變量綁定和一個(gè)可供應(yīng)用程序擴(kuò)展的標(biāo)準(zhǔn)函數(shù)庫(kù)。XPath 分析程序的任何實(shí)現(xiàn)都提供了一個(gè)用于對(duì)表達(dá)式進(jìn)行求值的函數(shù)庫(kù)。擴(kuò)展函數(shù)被定義在特定于供應(yīng)商的 XPath 實(shí)現(xiàn)中,但是也可以由專(zhuān)用的和基于 XPath 的編程 API(例如,XSL 轉(zhuǎn)換和 XPointer)提供。XPath 表達(dá)式通常會(huì)返回節(jié)點(diǎn)集,但是布爾值、字符串、數(shù)字和其他類(lèi)型也受到支持。
最常用的 XPath 表達(dá)式類(lèi)型是位置路徑。位置路徑是一個(gè)看起來(lái)與文件系統(tǒng)路徑非常類(lèi)似的表達(dá)式,它既可以是絕對(duì)路徑,也可以是相對(duì)于上下文節(jié)點(diǎn)的相對(duì)路徑。絕對(duì)位置路徑以正斜杠開(kāi)始。如圖 1 中所示,完全限定位置路徑由三部分組成:軸、節(jié)點(diǎn)測(cè)試以及一個(gè)或多個(gè)謂詞。軸信息定義表達(dá)式的初始上下文節(jié)點(diǎn)集,而節(jié)點(diǎn)測(cè)試是標(biāo)識(shí)該節(jié)點(diǎn)集中路徑的一系列節(jié)點(diǎn)名稱。謂詞是一個(gè)邏輯表達(dá)式,它定義了用于篩選當(dāng)前節(jié)點(diǎn)集的條件。
圖 1 位置路徑
XPath 表達(dá)式可以包含任何數(shù)量的謂詞。如果沒(méi)有指定任何謂詞,則在查詢中返回該上下文節(jié)點(diǎn)的所有子節(jié)點(diǎn)。否則,使用簡(jiǎn)捷的 AND 運(yùn)算符將用各種謂詞設(shè)置的條件邏輯地串聯(lián)在一起。請(qǐng)注意,謂詞按照它們出現(xiàn)的順序進(jìn)行處理,以便下一個(gè)謂詞對(duì)上一個(gè)謂詞生成的節(jié)點(diǎn)集起作用。
在 進(jìn)行處理時(shí),XPath 表達(dá)式在名為位置步驟的子表達(dá)式中被標(biāo)記化,并且每個(gè)表達(dá)式都分別被計(jì)算。XPath 處理程序以迭代方式傳遞在上一步中生成的子表達(dá)式和上下文節(jié)點(diǎn)集。它返回一個(gè)可能縮小的節(jié)點(diǎn)集,以便用作下一個(gè)子表達(dá)式的輸入?yún)?shù)。在該過(guò)程中,上下文節(jié) 點(diǎn)、位置和大小都可能變化,而變量和函數(shù)引用以及命名空間聲明保持不變。每個(gè)位置步驟實(shí)際上都是一個(gè)位置路徑,因此,可以根據(jù)需要以縮寫(xiě)形式或完全限定形 式來(lái)表示。位置步驟由正斜杠分隔。
在 .NET Framework 中,可以通過(guò) XmlNode 類(lèi)在 XmlDocument 類(lèi)中公開(kāi)的方法或者通過(guò) XPathNavigator 類(lèi)使用 XPath 表達(dá)式。
XPath 導(dǎo)航器
在 .NET Framework 中,XmlDocument 類(lèi)代表由 W3C 批準(zhǔn)的標(biāo)準(zhǔn) XML DOM(DOM 級(jí)別 2 標(biāo)準(zhǔn))。在從 XmlDocument 中到達(dá)的每個(gè)子節(jié)點(diǎn)上,都實(shí)現(xiàn)了幾個(gè)搜索方法。XmlNode 類(lèi)提供了 SelectNodes 和 SelectSingleNode 方法,它們使用 XPath 表達(dá)式在文檔中搜索節(jié)點(diǎn)。這些方法幾乎與基于 COM 的 MSXML 庫(kù)中具有類(lèi)似名稱的方法完全相同。SelectNodes 返回一系列對(duì)象,而 SelectSingleNode 只返回第一個(gè)匹配搜索條件的對(duì)象。以下是使用 SelectNodes 的方法:
Dim doc As XmlDocument = New XmlDocument()doc.Load(fileName)
Dim nodes As XmlNodeList
nodes = doc.SelectNodes(queryString)
SelectSingleNode 方法與 SelectNodes 的不同之處在于它返回單個(gè) XmlNode 對(duì)象。稍后,我將詳細(xì)討論這些方法。
對(duì) XPath 表達(dá)式的 XmlDocument 支持具有兩個(gè)目標(biāo)。它使從 MSXML COM 代碼到 .NET Framework 的轉(zhuǎn)換變得順暢,同時(shí)提供了一種在內(nèi)存映射 XML 文檔中搜索節(jié)點(diǎn)的內(nèi)置機(jī)制。然而,XmlDocument 查詢 API 是簡(jiǎn)單的高級(jí)別包裝。用于處理 XPath 表達(dá)式的核心 .NET Framework API 是圍繞 XPathNavigator 類(lèi)生成的。導(dǎo)航器是一個(gè) XPath 處理程序,它在任何公開(kāi) IXPathNavigable 接口的 XML 數(shù)據(jù)存儲(chǔ)區(qū)之上工作。導(dǎo)航器通過(guò) XPathNavigator 類(lèi)中定義的接口呈現(xiàn),它使用 Select 方法分析和執(zhí)行表達(dá)式。與 XmlDocument 方法不同,導(dǎo)航器接受以純文本形式提供的以及通過(guò)預(yù)編譯對(duì)象提供的表達(dá)式。可以從 XmlDocument 類(lèi)或 XPathDocument 類(lèi)中以編程方式訪問(wèn) XPathNavigator 對(duì)象。圖 2 說(shuō)明了兩種訪問(wèn) .NET Framework 中的 XPath 函數(shù)的方式。
圖 2 訪問(wèn) XPath 函數(shù)
以下代碼片段顯示了如何根據(jù) XML 文檔創(chuàng)建 XPathNavigator 以及如何執(zhí)行 XPath 查詢:
Dim doc As XPathDocumentDim nav As XPathNavigator
Dim iterator As XPathNodeIterator
doc = New XPathDocument(fileName)
nav = doc.CreateNavigator()
iterator = nav.Select(queryString)
While iterator.MoveNext()
' nav points to the node subtree
End While
導(dǎo)航器返回一個(gè) XPath 迭代器對(duì)象。迭代器只是用來(lái)在返回的節(jié)點(diǎn)集中移動(dòng)的專(zhuān)用枚舉器對(duì)象。稍后我將討論迭代器。
文檔、導(dǎo)航器和讀取器
在 .NET Framework 出現(xiàn)之前,處理 XML 文件涉及到處理按照 SAX 或 XmlDocument 規(guī)范呈現(xiàn)的文檔。
XPath 處理程序(它是分析和執(zhí)行 XPath 查詢的引擎)是在 XPathNavigator 類(lèi)的內(nèi)部生成的。如圖 2 所示,XPath 計(jì)算總是被委托給導(dǎo)航器,而不管高級(jí)別調(diào)用方 API 是哪個(gè)。導(dǎo)航器在特定的數(shù)據(jù)存儲(chǔ)區(qū)(通常是 XPathDocument 類(lèi)的實(shí)例)之上工作。只要數(shù)據(jù)存儲(chǔ)區(qū)類(lèi)實(shí)現(xiàn)了如下所示的 IXPathNavigable 接口,則也可以使用其他數(shù)據(jù)存儲(chǔ)區(qū):
public interface IXPathNavigable {XPathNavigator CreateNavigator();
}
除了 XPathDocument 類(lèi)以外,數(shù)據(jù)存儲(chǔ)區(qū)的示例還包括 XmlDocument 和 XmlDataDocument。
數(shù) 據(jù)存儲(chǔ)區(qū)負(fù)責(zé)提供導(dǎo)航器以探索內(nèi)存中的 XML 內(nèi)容。XPathNavigator 實(shí)現(xiàn)總是特定于存儲(chǔ)區(qū),并且通過(guò)從 XPathNavigator 抽象類(lèi)繼承生成。盡管實(shí)際上您總是通過(guò) XPathNavigator 的常見(jiàn)引用類(lèi)型來(lái)編寫(xiě)導(dǎo)航器,但每個(gè)數(shù)據(jù)存儲(chǔ)區(qū)類(lèi)都具有它自己的導(dǎo)航器對(duì)象。它們是內(nèi)部的未記錄的類(lèi),無(wú)法以編程方式訪問(wèn),并且通常以相當(dāng)不同的方式實(shí) 現(xiàn)。圖 3 列出了 .NET Framework 中定義的三個(gè) XPath 數(shù)據(jù)存儲(chǔ)區(qū)所使用的真實(shí)導(dǎo)航器類(lèi)。特定于文檔的導(dǎo)航器利用文檔類(lèi)的內(nèi)部布局以便提供導(dǎo)航 API。
XPathDocument 類(lèi)為 XML 文檔提供高度優(yōu)化的、只讀的內(nèi)存中存儲(chǔ)區(qū)。該類(lèi)是專(zhuān)門(mén)設(shè)計(jì)以實(shí)現(xiàn) XPath 數(shù)據(jù)模型的,它沒(méi)有為節(jié)點(diǎn)提供任何標(biāo)識(shí)。它只是創(chuàng)建一個(gè)基礎(chǔ)的節(jié)點(diǎn)引用樹(shù),以便讓導(dǎo)航器能夠快速和有效地操作。XPathDocument 類(lèi)的內(nèi)部體系結(jié)構(gòu)看起來(lái)像是節(jié)點(diǎn)引用鏈表。節(jié)點(diǎn)是通過(guò)一個(gè)代表 XmlNode 類(lèi)的小型子集的內(nèi)部類(lèi) (XPathNode) 來(lái)管理的(參見(jiàn)圖 2)。
與 XPathDocument 相比,XmlDocument 類(lèi)提供了對(duì)基礎(chǔ) XML 文檔的節(jié)點(diǎn)的讀寫(xiě)訪問(wèn)。此外,還可以分別訪問(wèn)每個(gè)節(jié)點(diǎn)。
XmlDocument 類(lèi)還提供了創(chuàng)建導(dǎo)航器對(duì)象的能力,如下所示:
Dim doc As XmlDocument = New XmlDocument()doc.Load(fileName)
Dim nav As XPathNavigator = doc.CreateNavigator()
XmlDocument 的導(dǎo)航器類(lèi)實(shí)現(xiàn)了 IHasXmlNode 接口。該接口定義了一個(gè)方法,即 GetNode:
public interface IHasXmlNode {XmlNode GetNode();
}
使用該方法,調(diào)用方可以基于 XPathNavigator 的位置訪問(wèn)和詢問(wèn) XmlDocument 中當(dāng)前選擇的節(jié)點(diǎn)。無(wú)法為基于 XPathDocument 的導(dǎo)航器實(shí)現(xiàn)該功能,原因僅僅在于它不像 XmlDocument 類(lèi)那樣通過(guò) XmlNode 類(lèi)公開(kāi)內(nèi)部結(jié)構(gòu)。這是設(shè)計(jì)使然。XPathDocument 最大限度地減小了內(nèi)存足跡,并且不提供節(jié)點(diǎn)標(biāo)識(shí)。
因?yàn)?GetNode 方法是在 XmlDocument 上的 XPathNavigator 類(lèi)上實(shí)現(xiàn)的,所以調(diào)用方可以通過(guò)類(lèi)型轉(zhuǎn)換來(lái)利用它,如以下代碼片段所示:
Dim doc As XmlDocument = New XmlDocument()doc.Load(fileName)
Dim nav As XPathNavigator = doc.CreateNavigator()
Dim i As XPathNodeIterator = nav.Select(query)
Dim node As XmlNode = CType(i.Current, IHasXmlNode).GetNode()
此時(shí),調(diào)用方程序已經(jīng)獲得了對(duì)該節(jié)點(diǎn)的完全訪問(wèn)權(quán)限,并且可以任意讀取和更新它。
最 后,XmlDataDocument 類(lèi)是 XmlDocument 的擴(kuò)展,其目標(biāo)是允許通過(guò) XML 操作關(guān)系數(shù)據(jù)集。這是一個(gè)簡(jiǎn)潔的示例,它說(shuō)明了 .NET Framework 導(dǎo)航 API 可以應(yīng)用于基于 XML 的數(shù)據(jù)以及類(lèi)似于 XML(數(shù)據(jù)的結(jié)構(gòu)類(lèi)似于 XML,但不是 XML)的數(shù)據(jù)這樣一個(gè)事實(shí)。
如果您查看 XPath 導(dǎo)航器的 MSDN? 文檔,則將看到導(dǎo)航器以類(lèi)似于游標(biāo)的方式(向前和向后)從基于 XML 的數(shù)據(jù)存儲(chǔ)區(qū)中讀取數(shù)據(jù),并且提供對(duì)基礎(chǔ)數(shù)據(jù)的只讀訪問(wèn)。此外,它保持有關(guān)當(dāng)前節(jié)點(diǎn)的信息,并且使用多種移動(dòng)方法推進(jìn)內(nèi)部指針。當(dāng)導(dǎo)航器定位于給定的節(jié)點(diǎn) 時(shí),它的所有屬性都將反映該節(jié)點(diǎn)的值。當(dāng)然,這類(lèi)似于 XML 讀取器(我在 MSDN Magazine 的 2003 年 5 月刊的一篇文章中對(duì)它進(jìn)行了討論。那么,導(dǎo)航器和讀取器之間有什么區(qū)別呢?
導(dǎo) 航器和讀取器是不同的東西。讀取器類(lèi)似于水龍帶游標(biāo),它提供基本的只讀、只進(jìn)移動(dòng)。導(dǎo)航器也是只讀的,但是它們提供了一組豐富得多的移動(dòng)方法(包括向前和 向后選項(xiàng))。導(dǎo)航器還提供了多個(gè)選擇方法以對(duì)搜索進(jìn)行微調(diào)。讀取器是更低級(jí)別的工具,可以用來(lái)讀取基于 XML 或類(lèi)似于 XML 的數(shù)據(jù),以及生成內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。XML 讀取器可以用來(lái)生成導(dǎo)航器所依賴的內(nèi)存中數(shù)據(jù)結(jié)構(gòu)。
查詢節(jié)點(diǎn)
假設(shè)您需要在基 于 .NET Framework 的應(yīng)用程序中實(shí)現(xiàn) XPath 查詢。是應(yīng)當(dāng)使用 XPath 導(dǎo)航器,還是最好堅(jiān)持使用 XmlDocument 的節(jié)點(diǎn)接口呢?XmlNode 的 SelectNodes 方法在內(nèi)部使用導(dǎo)航器對(duì)象來(lái)檢索匹配節(jié)點(diǎn)列表。隨后,使用導(dǎo)航器的 Select 方法的返回值以便初始化一個(gè)類(lèi)型為 XPathNodeList(它定義在 System.Xml.XPath 命名空間中)的內(nèi)部節(jié)點(diǎn)列表對(duì)象。正像您可能已經(jīng)猜到的那樣,該類(lèi)從已記錄的 XmlNodeList 類(lèi)繼承。此外,與 SelectNodes 不同的是,導(dǎo)航器可以完全利用已編譯的表達(dá)式。
SelectNodes 方法總是以純文本形式接受 XPath 表達(dá)式。字符串隨后被逐字地傳遞給導(dǎo)航器。只有在一種情況下,基礎(chǔ)導(dǎo)航器才會(huì)收到已編譯的表達(dá)式。如果您使用處理命名空間信息的 SelectNodes 方法重載,則會(huì)首先編譯 XPath 表達(dá)式,然后將其傳遞給處理程序。重載方法的原型如下所示:
Function SelectNodes( _xpathExpr As String, _
nsm As XmlNamespaceManager) _
As XmlNodeList
當(dāng)您在會(huì)話中頻繁重用該表達(dá)式時(shí),使用已編譯表達(dá)式的優(yōu)點(diǎn)變得非常明顯,并且它們具有命名空間識(shí)別功能。XmlNamespaceManager 類(lèi)允許用戶指定要綁定的命名空間的前綴。
SelectSingleNode 方法是 SelectNodes 的特例,它只返回所返回節(jié)點(diǎn)集中的第一個(gè)元素。遺憾的是,直到現(xiàn)在,SelectSingleNode 的實(shí)現(xiàn)也不是特別有效。如果您只需要找到第一個(gè)匹配節(jié)點(diǎn),則調(diào)用 SelectSingleNode 或 SelectNodes 幾乎完全相同。而且,如果您需要盡一切可能提高性能,則使用 SelectNodes 可能更好。下面的偽代碼說(shuō)明了 SelectSingleNode 的當(dāng)前實(shí)現(xiàn):
Function SelectSingleNode(xpathExpr As String) As XmlNodeDim nodes As XmlNodeList = SelectNodes(xpathExpr)
Return nodes(0)
End Function
該方法在內(nèi)部調(diào)用 SelectNodes,并且返回第一個(gè)匹配節(jié)點(diǎn)。但是,請(qǐng)注意,XmlNodeList 是動(dòng)態(tài)生成的 — 只有在收到請(qǐng)求時(shí),才會(huì)搜索下一個(gè)節(jié)點(diǎn)。
一種更好的查詢單個(gè)節(jié)點(diǎn)的方式是,向 SelectNodes 傳遞一個(gè)能夠返回單個(gè)節(jié)點(diǎn)的更為精確的 XPath 表達(dá)式。其思想是避免使用如下所示的一般性通配符表達(dá)式:
NorthwindEmployees/Employee您應(yīng)當(dāng)在 XPath 表達(dá)式上放置一個(gè)更強(qiáng)的篩選器,以便它返回大小正確的節(jié)點(diǎn)子集。要只獲得第一個(gè)節(jié)點(diǎn),請(qǐng)?zhí)砑右粋€(gè)額外的謂詞,以便在找到第一個(gè)匹配項(xiàng)之后停止查詢:
NorthwindEmployees/Employee[position() = 1]這通常是 XPath 最佳做法,并且并不特別歸因于 .NET Framework 實(shí)現(xiàn)。可以對(duì)該方法進(jìn)行提煉和概括,以便根據(jù)需要調(diào)整節(jié)點(diǎn)集的大小。下面的查詢字符串顯示了如何獲得頭 n 個(gè)匹配的節(jié)點(diǎn):
NorthwindEmployees/Employee[position() < n+1]當(dāng)您需要對(duì)采用某種形式的節(jié)點(diǎn)標(biāo)識(shí)的選定節(jié)點(diǎn)執(zhí)行操作時(shí),我建議您使用 XmlNode 的 SelectNodes 而不是 XPathNavigator 的實(shí)例來(lái)執(zhí)行 XPath 查詢。如果您需要將該節(jié)點(diǎn)作為 XmlNode 類(lèi)的實(shí)例進(jìn)行進(jìn)一步的管理,則使用 SelectNodes 可以簡(jiǎn)化代碼。
用 XPathNavigator 編程
讓我們?cè)賹W(xué)習(xí)一點(diǎn)兒有關(guān) XPathNavigator 的編程接口的知識(shí)。通常,不管使用哪個(gè)應(yīng)用程序級(jí)別的 API,對(duì) XML 數(shù)據(jù)源執(zhí)行 XPath 查詢所需的步驟序列大致是相同的:
獲得對(duì)支持 XPath 的文檔類(lèi)的引用(例如,XPathDocument 或 XmlDocument 類(lèi)的實(shí)例)。
為指定的數(shù)據(jù)存儲(chǔ)區(qū)創(chuàng)建一個(gè)導(dǎo)航器對(duì)象。
如果您計(jì)劃以后重用 XPath 表達(dá)式,則還可以對(duì)其進(jìn)行預(yù)編譯。
調(diào)用導(dǎo)航器的 Select 方法以采取操作。
導(dǎo) 航器對(duì)象的編程接口定義在 XPathNavigator 抽象類(lèi)中。盡管您通常使用導(dǎo)航器對(duì)象來(lái)執(zhí)行 XPath 查詢,但讓 XPathNavigator 類(lèi)代表更為通用的組件是不值得的。導(dǎo)航器是一個(gè)一般性的接口,它充當(dāng)類(lèi)似于游標(biāo)的探測(cè)器,以探測(cè)任何將其內(nèi)容作為 XML 公開(kāi)的數(shù)據(jù)存儲(chǔ)區(qū)。盡管在功能上類(lèi)似于 XML 讀取器,但對(duì)于簡(jiǎn)單的讀取操作而言,導(dǎo)航器卻沒(méi)有前者快速和有效,這是因?yàn)樗且粋€(gè)樹(shù)導(dǎo)航器,并且專(zhuān)門(mén)用于進(jìn)行檢索。如果您只需要讀取 XML 文檔,請(qǐng)使用 XML 讀取器;如果您需要執(zhí)行查詢,請(qǐng)使用導(dǎo)航器。請(qǐng)記住,現(xiàn)在導(dǎo)航器在完全內(nèi)存映射數(shù)據(jù)源上工作。
從功能上 說(shuō),XPathNavigator 類(lèi)與只是將導(dǎo)航文檔內(nèi)容所需的所有方法組合在一起的偽類(lèi)沒(méi)有太大不同。較大的差異在于,XPathNavigator 是一個(gè)與文檔類(lèi)完全分離的獨(dú)特組件。換句話說(shuō),XPathNavigator 代表某種已經(jīng)映射到 XML 數(shù)據(jù)模型的數(shù)據(jù)存儲(chǔ)區(qū)的 XML 視圖。
圖 4 枚舉了 XPathNavigator 類(lèi)上的可用屬性。像 XML 讀取器和 XMLDocument 類(lèi)一樣,XPathNavigator 利用了名稱表來(lái)更有效地存儲(chǔ)重復(fù)字符串。該屬性集看起來(lái)像是表征 XmlTextReader 類(lèi)中當(dāng)前節(jié)點(diǎn)的屬性的子集。
值得重復(fù)說(shuō)明的是,XPathNavigator 的 Select 方法返回一個(gè)迭代器對(duì)象,該對(duì)象的當(dāng)前元素被作為導(dǎo)航器(XPathNavigator 類(lèi))向后引用。要訪問(wèn)和處理節(jié)點(diǎn)信息,您只能使用該導(dǎo)航器的屬性。圖 4 中的屬性是只讀的,并且更為重要的是,它們沒(méi)有映射到 XmlNode 類(lèi)的實(shí)例。如果您需要將該節(jié)點(diǎn)作為 XmlNode 對(duì)象進(jìn)行操作(例如,為了應(yīng)用更改),以確保將 XmlDocument 用作數(shù)據(jù)存儲(chǔ)區(qū)類(lèi),然后將迭代器的當(dāng)前元素轉(zhuǎn)換為 IHasXmlNode。從引用類(lèi)型中,IHasXmlNode 調(diào)用 GetNode 方法,該方法返回基礎(chǔ)節(jié)點(diǎn)的 XmlNode 實(shí)例。在其他所有情況下,對(duì)節(jié)點(diǎn)的訪問(wèn)權(quán)限是只讀的。
導(dǎo)航器對(duì)象提供了一組豐富的方法,我基于這些方法的功能將它們劃分為三個(gè)主要的組:選擇、移動(dòng)和雜項(xiàng)。以下代碼片段選擇節(jié)點(diǎn)的子孫。用于獲得祖先的代碼幾乎完全相同:
Dim doc As XPathDocument = New XPathDocument(fileName)Dim nav As XPathNavigator = doc.CreateNavigator()
nav.SelectDescendants(nodeName, nsUri, selfIncluded)
SelectDescendants 采用該節(jié)點(diǎn)的本地名稱并選擇子孫。NsUri 變量指示子孫節(jié)點(diǎn)的命名空間 URI(如果存在的話)。SelfIncluded 布爾型變量是一個(gè)標(biāo)志,它指示是否應(yīng)該將該上下文節(jié)點(diǎn)包括在節(jié)點(diǎn)集中。
圖 5 包含了 XPathNavigator 的移動(dòng)方法的列表。您可以按照命名空間限制,向任一方向跳躍 — 向前或向后(從同輩到同輩)。您可能已經(jīng)注意到,有三組不同的移動(dòng)方法,它們分別適用于元素、屬性和命名空間節(jié)點(diǎn)。只有 MoveTo 和 MoveToRoot 方法可以在任何節(jié)點(diǎn)(不管類(lèi)型如何)上調(diào)用。此外,屬性和命名空間還具有用于返回其值的方法:GetAttribute 和 GetNamespace。當(dāng)被選擇時(shí),導(dǎo)航器的 Name 屬性返回命名空間前綴。Value 屬性返回 URI。
圖 6 對(duì) XPathNavigator 類(lèi)上定義的其他所有方法進(jìn)行了分組。其中幾個(gè)方法與 XPath 表達(dá)式有關(guān)。XPath 表達(dá)式是一個(gè)代表位置路徑的字符串(盡管它不僅僅是普通的命令字符串)。它具有由 XPathExpression 類(lèi)封裝的環(huán)繞上下文。表達(dá)式的上下文包括返回類(lèi)型和命名空間信息。XPathExpression 類(lèi)不是可公開(kāi)創(chuàng)建的。要獲得它的新實(shí)例,必須取得一個(gè) XPath 字符串表達(dá)式并將其編譯為 XPathExpression 對(duì)象。下面的代碼片段顯示了如何編譯表達(dá)式并顯示它的預(yù)期返回類(lèi)型:
Dim expr As XPathExpression = nav.Compile(xpathExpr)Console.WriteLine(expr.ReturnType.ToString())
nav.Select(expr)
已編譯的 XPath 表達(dá)式可以由 Select、Evaluate 和 Matches 方法使用。這里的術(shù)語(yǔ)“編譯”并不意味著 XPath 表達(dá)式成為可執(zhí)行的表達(dá)式。更簡(jiǎn)單地說(shuō)來(lái),必須將編譯操作視為通過(guò)收集各種信息片段產(chǎn)生對(duì)象的過(guò)程。表達(dá)式可以返回除節(jié)點(diǎn)集以外的各種類(lèi)型的值。在這種情 況下,使用 Evaluate 方法計(jì)算該表達(dá)式,然后將返回的一般對(duì)象轉(zhuǎn)換為特定的類(lèi)型。Select 是一個(gè)更為特殊的方法,因?yàn)樗俣ǚ祷仡?lèi)型是節(jié)點(diǎn)集并且將這些節(jié)點(diǎn)插入到迭代器中。
對(duì)節(jié)點(diǎn)集進(jìn)行排序
XPathExpression 類(lèi)中內(nèi)置的一個(gè)有趣的擴(kuò)展是能夠在將節(jié)點(diǎn)集傳回調(diào)用方之前對(duì)其進(jìn)行排序。要添加排序算法,需要調(diào)用 XPathExpression 對(duì)象上的 AddSort 方法。AddSort 具有兩種形式:
Sub AddSort(expr As Object, comparer As IComparer)Sub AddSort(expr As Object, order As XmlSortOrder, _
caseOrder As XmlCaseOrder, lang As String, _
dataType As XmlDataType)
Expr 參數(shù)表示排序關(guān)鍵字。它可以是表示節(jié)點(diǎn)名稱的字符串,也可以是另外一個(gè)能夠計(jì)算為節(jié)點(diǎn)名稱的 XPathExpression 對(duì)象。在第一個(gè)重載中,comparer 參數(shù)引用實(shí)現(xiàn)了 IComparer 接口的類(lèi)的一個(gè)實(shí)例。該接口提供了 Compare 方法,該方法實(shí)際上用于比較一對(duì)值。如果您需要指定一個(gè)自定義算法以便對(duì)節(jié)點(diǎn)進(jìn)行排序,則請(qǐng)使用該重載。
第二個(gè)重載總 是根據(jù) dataType 參數(shù)的值執(zhí)行數(shù)值或文本比較。此外,您還可以指定排序順序(升序或降序),甚至指定大寫(xiě)和小寫(xiě)字母的排序順序(或者,您可以通過(guò)使用值 XmlCaseOrder.None 完全忽略大寫(xiě))。最后,lang 參數(shù)指定要使用哪種語(yǔ)言進(jìn)行比較。語(yǔ)言名稱還應(yīng)當(dāng)指定區(qū)域設(shè)置。例如,要指示美國(guó)英語(yǔ),最好使用“us-en”而不是簡(jiǎn)單地使用“en”。
讓 我們更深入地討論一下 IComparer 接口。為了對(duì)對(duì)象數(shù)組進(jìn)行排序,.NET Framework 提供了幾個(gè)預(yù)定義的比較器類(lèi),包括 Comparer 和 CaseInsensitiveComparer。比較器類(lèi)相對(duì)于大小寫(xiě)比較對(duì)象(通常為字符串)。CaseInsensitiveComparer 完成相同的工作,但忽略大小寫(xiě)。要在代碼中使用這兩個(gè)類(lèi),請(qǐng)確保導(dǎo)入 System.Collections 命名空間。Comparer 類(lèi)不具有公共構(gòu)造函數(shù),但是通過(guò) Default 靜態(tài)屬性提供了單個(gè)實(shí)例。例如:
expr.AddSort("lastname", Comparer.Default);如果需要,您還可以創(chuàng)建自己的比較器類(lèi)。以下代碼顯示了一個(gè)相當(dāng)平常的 Visual Basic? .NET 實(shí)現(xiàn):
Class MyOwnStringComparerImplements IComparer
Public Function Compare(x As Object, y As Object) _
As Integer Implements IComparer.Compare
Dim strX As String = CType(x, String)
Dim strY As String = CType(y, String)
Return String.Compare(strX, strY)
End Sub
End Class
The Compare method should如果兩個(gè)字符串相等,則 Compare 方法應(yīng)當(dāng)返回 0;如果 x 先于 y,則該方法返回一個(gè)大于 0 的值;如果 y 先于 x,則它返回一個(gè)負(fù)值。
該類(lèi)還可以定義在應(yīng)用程序的體中,并且不需要單獨(dú)的程序集。以下代碼將一個(gè)自定義比較器與一個(gè) XPath 表達(dá)式相關(guān)聯(lián):
Dim comp As MyOwnStringComparer = New MyOwnStringComparer()expr.AddSort("lastname", comp)
圖 7 顯示了該技術(shù)的一個(gè)應(yīng)用。在為 XML 文檔創(chuàng)建導(dǎo)航器之后,示例控制臺(tái)應(yīng)用程序編譯要使用的表達(dá)式。假設(shè)的數(shù)據(jù)源具有以下架構(gòu):
<MyDataSet><NorthwindEmployees>
<Employee>
<employeeid>...</employeeid>
<lastname>...</lastname>
<firstname>...</firstname>
<title>...</title>
</Employee>
???
</NorthwindEmployees>
</MyDataSet>
按照單個(gè)節(jié)點(diǎn)進(jìn)行排序是容易的 — 只須將節(jié)點(diǎn)名傳遞給 AddSort 方法。按照多個(gè)字段進(jìn)行排序更為復(fù)雜。其思想是指示一個(gè)由逗號(hào)分隔的節(jié)點(diǎn)名稱列表。但是,參數(shù)字符串必須是一個(gè)能夠計(jì)算為由逗號(hào)分隔的節(jié)點(diǎn)名稱列表的 XPath 表達(dá)式。如果簡(jiǎn)單地將排序關(guān)鍵字指定為類(lèi)似于“title, lastname”的形式,則您將獲得運(yùn)行庫(kù)錯(cuò)誤,因?yàn)?XPath 處理程序錯(cuò)誤地將其當(dāng)作實(shí)際的節(jié)點(diǎn)名稱。真正需要的是 XPath 處理程序能夠在運(yùn)行時(shí)將其轉(zhuǎn)換為所需頭銜和姓氏的表達(dá)式,如下所示:
Dim sortKey As String = "concat(concat(title, ','), lastname)"Concat 關(guān)鍵字標(biāo)識(shí) XPath 實(shí)現(xiàn)提供的預(yù)定義 Helper 函數(shù)之一。
在圖 7 中,您還可以了解如何有效地使用迭代器。在使用 XPath迭代器時(shí)需要注意的一個(gè)要點(diǎn)是,代碼一次遍歷節(jié)點(diǎn)集中的所有節(jié)點(diǎn)。
您可能需要深入到其中每個(gè)節(jié)點(diǎn)的子樹(shù)中。為此,請(qǐng)首先克隆導(dǎo)航器以避免放錯(cuò)主要的導(dǎo)航器,從而損害最外層的循環(huán):
Dim iterator As XPathNodeIterator = nav.Select(expr)While iterator.MoveNext()
Dim nav2 As XPathNavigator = iterator.Current.Clone()
nav2.MoveToFirstChild()
???
nav2.MoveToNext()
???
End While
迭代器上的 Current 屬性返回節(jié)點(diǎn)集中的當(dāng)前節(jié)點(diǎn)。它計(jì)算為 XPathNavigator 類(lèi)的實(shí)例,并且可以使用 Clone 方法克隆。
.NET 中的 XSL 概述
XML 轉(zhuǎn)換是用戶定義的、試圖用另外的(等價(jià)的)語(yǔ)法表達(dá)給定文檔語(yǔ)義的算法。轉(zhuǎn)換過(guò)程包含基于樣式表的結(jié)構(gòu)來(lái)呈現(xiàn)源文檔。樣式表是聲明性的用戶定義文檔,它包 含用來(lái)將一個(gè)文檔轉(zhuǎn)換為另一個(gè)文檔的規(guī)則集。XSL 是指為了表示 XML 文檔的樣式表而設(shè)計(jì)的元語(yǔ)言。XSL 文件最初被想像為 HTML 級(jí)聯(lián)樣式表 (CSS) 的 XML 對(duì)應(yīng)物。鑒于此,XSL 被設(shè)計(jì)為可擴(kuò)展的、用戶可定義的工具,該工具可以用 HTML 呈現(xiàn) XML 文檔以便進(jìn)行顯示。樣式表日益增長(zhǎng)的復(fù)雜性以及 XML 架構(gòu)的出現(xiàn)導(dǎo)致了 XSLT 的產(chǎn)生。目前,XSL 只是許多派生技術(shù)的一個(gè)總括性的術(shù)語(yǔ),所有這些技術(shù)能夠更好地形容和實(shí)現(xiàn)將 XML 文檔樣式化的的原始思想。XSL 所包含的各種組件是在代碼中使用的實(shí)際軟件實(shí)體:XSLT、XPath 和 XSL 格式化對(duì)象 (XSLFO)。
XSLT 程序是一個(gè)一般性的轉(zhuǎn)換規(guī)則集,其輸出可以是任何基于文本的語(yǔ)言,包括 HTML、RTF 等等。正如前文提到的那樣,XPath 是一種查詢語(yǔ)言,XSLT 程序可以利用它來(lái)選擇源 XML 文檔的特定部分。XPath 表達(dá)式的結(jié)果隨后由 XSLT 處理程序進(jìn)行分析和闡述。通常,XSLT 處理程序?qū)υ次臋n進(jìn)行順序處理,但是如果要求訪問(wèn)特定的節(jié)點(diǎn)組,則它會(huì)將該源文檔傳遞給 XPath。
在 .NET Framework 中,XSLT 的核心類(lèi)是 XslTransform。該類(lèi)位于 System.Xml.Xsl 命名空間中,并實(shí)現(xiàn)了 XSLT 處理程序。可以采取兩個(gè)步驟來(lái)使用該類(lèi):首先在處理程序中加載樣式表,然后根據(jù)需要向任意多的源文檔應(yīng)用轉(zhuǎn)換。XslTransform 類(lèi)只支持 XSLT 1.0 規(guī)范。圖 8 中的 C# 代碼實(shí)現(xiàn)了一個(gè)命令行 XSLT 轉(zhuǎn)換器。它采用三個(gè)命令行參數(shù)(XML 源、XSLT 樣式表和輸出文件)來(lái)設(shè)置處理程序并且將轉(zhuǎn)換結(jié)果保存到輸出文件中。
XslTransform 類(lèi)
XslTransform 類(lèi)提供了兩個(gè)特定于其活動(dòng)的方法 — Load 和 Transform。該類(lèi)只有在轉(zhuǎn)換操作期間才能保證以線程安全的方式進(jìn)行操作。換句話說(shuō),盡管該類(lèi)的實(shí)例可以由多個(gè)線程共享,但只有 Transform 方法才可以從多個(gè)線程中安全地調(diào)用。Load 方法不是線程安全的。Transform 方法讀取共享狀態(tài),并且可以從多個(gè)線程中并發(fā)運(yùn)行。圖 9 顯示了該類(lèi)的內(nèi)部體系結(jié)構(gòu)的一部分。在加載樣式表之后,XSLT 處理程序需要修改它的狀態(tài)以反映所加載的文檔。該操作不會(huì)在由鎖定語(yǔ)句創(chuàng)建的虛擬邊界中以原子方式發(fā)生。因此,并發(fā)運(yùn)行的線程理論上可以訪問(wèn)同一個(gè) XSLT 處理程序?qū)嵗?#xff0c;從而破壞數(shù)據(jù)一致性。加載操作是線程敏感的,因?yàn)樗淖兞藢?duì)象的全局狀態(tài)。
圖 9 XSL Transform 類(lèi)
在 .NET Framework 的版本 1.0 中,XslTransform 類(lèi)附加了鏈接請(qǐng)求權(quán)限集。鏈接請(qǐng)求指定直接調(diào)用方運(yùn)行代碼必須具有的權(quán)限。調(diào)用方權(quán)限在即時(shí)編譯期間進(jìn)行檢查:
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]public sealed class XslTransform
{ ??? }
XslTransform 類(lèi)的權(quán)限集屬性由名稱表示,并且指向內(nèi)置的權(quán)限集之一 — FullTrust。這對(duì)用戶來(lái)說(shuō)意味著什么呢?只有對(duì)所有本地資源都具有完全受信任訪問(wèn)權(quán)限的調(diào)用方(該檢查涉及直接調(diào)用方,而不是調(diào)用方的調(diào)用方)才 可以安全地調(diào)用到 XSLT 處理程序中。例如,如果您通過(guò)網(wǎng)絡(luò)共享調(diào)用 XSL 處理程序,則會(huì)引發(fā)安全異常。在 .NET Framework 的版本 1.1 中,該權(quán)限集已經(jīng)被移除。
在 .NET Framework XSLT 處理程序的總體行為中,可以明確地標(biāo)識(shí)三個(gè)階段:樣式表文檔的加載、內(nèi)部狀態(tài)的設(shè)置和轉(zhuǎn)換。頭兩個(gè)階段發(fā)生在 Load 方法的上下文中。當(dāng)然,在前面的 Load 調(diào)用成功終止之前,無(wú)法調(diào)用 Transform 方法。Load 方法總是同步工作,以便當(dāng)它返回時(shí),您可以確保加載步驟已經(jīng)實(shí)際完成。您將不會(huì)收到任何指示操作失敗或成功的返回值。但是,每當(dāng) Load 方法發(fā)生什么錯(cuò)誤時(shí),都會(huì)引發(fā)一些異常。特別地,如果您指向一個(gè)丟失的樣式表,則將得到 FileNotFoundException 類(lèi)型的異常;如果 XSLT 腳本包含某些錯(cuò)誤,則將得到 XsltCompileException 類(lèi)型的更一般的異常。XsltCompileException 異常提供了樣式表中發(fā)生錯(cuò)誤的行位置和編號(hào)。
可以從四個(gè)不同的源加載輸入樣式表:URL、XML 讀取器、XPathDocument 和 XPathNavigator。不管是哪個(gè)源,Load 方法的第一個(gè)操作都是將該源表示為一個(gè) XPathNavigator 對(duì)象。樣式表必須進(jìn)行編譯,并且,考慮到編譯器的體系結(jié)構(gòu),導(dǎo)航器是非常有效的對(duì)象。“編譯”是這樣一個(gè)過(guò)程,它簡(jiǎn)單地從原始樣式表中提取一些信息,并將 其存儲(chǔ)在方便的數(shù)據(jù)結(jié)構(gòu)中以便進(jìn)一步使用。這些數(shù)據(jù)結(jié)構(gòu)的整個(gè)集合稱為 XSLT 處理程序的狀態(tài)。圖 10 顯示了 Load 方法的流程。
圖 10 Load 方法流程
樣式表編譯器用從源中讀取的數(shù)據(jù)填充三個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)。作為已編譯樣式表對(duì)象引用的對(duì)象表示樣式表內(nèi)容的一種索引。其他兩個(gè)對(duì)象是表和操作,前者包含要執(zhí)行的 XPath 查詢的編譯版本,后者是各種模板所需的操作。
Transform 方法至少采用兩個(gè)顯式參數(shù) — 源 XML 文檔和輸出流,再加上幾個(gè)隱式參數(shù)。當(dāng)然,已編譯的樣式表對(duì)象是隱式輸入?yún)?shù)之一。第二個(gè)隱式參數(shù)是 XslTransform 的 XmlResolver 屬性(該屬性專(zhuān)門(mén)用于解析外部資源)的內(nèi)容。Transform 方法還可以采用第三個(gè)顯式參數(shù) — 類(lèi) XsltArgumentList 的對(duì)象。該參數(shù)包含被用作轉(zhuǎn)換過(guò)程的輸入的命名空間限定參數(shù)。
XML 源文檔被標(biāo)準(zhǔn)化為 XPathNavigator,并且向下傳遞給 XSLT 處理程序。有趣的是,Transform 方法具有兩種類(lèi)型的重載。其中一些重載作為 void 方法工作,并且只是寫(xiě)入指定的流。其他重載作為函數(shù)工作,并且明確返回一個(gè) XML 讀取器對(duì)象。正如我稍后將討論的那樣,該功能提供了一個(gè)非常有趣的機(jī)會(huì):實(shí)現(xiàn)異步 XSLT 轉(zhuǎn)換。在圖 11 中,可以看到 Transform 方法的執(zhí)行流。
圖 11 Transform 方法
Transform 方法還使您可以使用 XsltArgumentList 類(lèi)的實(shí)例向樣式表傳遞參數(shù)。在以這種方式向 XSLT 腳本傳遞參數(shù)時(shí),您無(wú)法指定哪個(gè)模板調(diào)用將實(shí)際使用這些參數(shù)。您只是以全局方式將參數(shù)傳遞給 XSLT 處理程序。負(fù)責(zé)處理模板的內(nèi)部模塊隨后將根據(jù)需要讀取和導(dǎo)入這些參數(shù):
XsltArgumentList args = new XsltArgumentList();args.AddParam("MaxNumOfRows", "", 7);
AddParam 方法在參數(shù)列表中創(chuàng)建了一個(gè)新項(xiàng)。該方法需要三個(gè)參數(shù):參數(shù)名稱、命名空間 URI(如果該名稱由命名空間前綴限定)以及一個(gè)代表實(shí)際值的對(duì)象。不管您用來(lái)將參數(shù)值包裝到參數(shù)列表中的 CLR 類(lèi)型如何,該參數(shù)值都必須對(duì)應(yīng)于有效的 XPath 類(lèi)型:字符串、布爾型、數(shù)字、節(jié)點(diǎn)片段和節(jié)點(diǎn)集。數(shù)字對(duì)應(yīng)于雙精度類(lèi)型,而節(jié)點(diǎn)片段和節(jié)點(diǎn)集等價(jià)于 XPathNavigators 和 XPath 節(jié)點(diǎn)迭代器。
參數(shù)和擴(kuò)展對(duì)象
XsltArgumentList 不是基于集合的類(lèi)。它不是派生自集合類(lèi),也沒(méi)有實(shí)現(xiàn)任何典型的列表接口(如 IList 或 ICollection)。XsltArgumentList 類(lèi)是圍繞幾個(gè)哈希表生成的 — 一個(gè)用于存放 XSLT 參數(shù),一個(gè)用于收集擴(kuò)展對(duì)象。擴(kuò)展對(duì)象是 .NET 對(duì)象的一個(gè)活動(dòng)實(shí)例,它可以作為參數(shù)傳遞給樣式表。例如,可以用各種方式擴(kuò)展 XSLT 腳本以獲得嵌入式 XPath 庫(kù)未提供的功能。XslTransform 類(lèi)支持 <xsl:eval> 指令,該指令使您可以將 VBScript 插入到樣式表中。將自定義代碼嵌入到腳本中的替代方法是使用 <msxsl:script> 元素。該新指令支持托管語(yǔ)言并且提供對(duì)整個(gè) .NET Framework 的訪問(wèn):
<msxsl:scriptlanguage = "language"
implements-prefix = "prefix">
???
</msxsl:script>
受支持的語(yǔ)言包括 C#、Visual Basic 和 JScript?。Language 屬性不是強(qiáng)制性的,并且,如果未指定該屬性,則默認(rèn)為 JScript。但是,Implements-prefix 屬性是強(qiáng)制性的。它聲明一個(gè)命名空間并且將用戶定義的代碼與它相關(guān)聯(lián)。該命名空間必須在樣式表中某處進(jìn)行定義。此外,要使用 <msxsl:script> 指令,該樣式表必須包含以下命名空間:
xmlns:msxsl=urn:schemas-microsoft-com:xslt要定義簡(jiǎn)單腳本,需要在該樣式表的根中聲明額外的命名空間。例如:
<xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:dino="urn:dino-scripts">
該聲明是調(diào)用 <msxsl:script> 指令所需要的。該命名空間只是將某些由用戶定義的腳本組合在一起。現(xiàn)在,需要使用前綴“dino”來(lái)限定對(duì) <msxsl:script> 塊中定義的任何函數(shù)的任何調(diào)用。在樣式表的體中,調(diào)用 <msxsl:script> 塊中定義的任何函數(shù):
<xsl:template match="lastname"><TD style="border:1px solid black">
<xsl:value-of select="dino:PrepareName(., ../firstname)" />
</TD>
</xsl:template>
如果您在參數(shù)的前后加上引號(hào),則它們將被視為文字值。要確保托管函數(shù)收到節(jié)點(diǎn)值,請(qǐng)使用與用于 <xsl:value of> 指令的 select 屬性的表達(dá)式相同的表達(dá)式。
<msxsl:script> 指令并不總是最佳的解決方案。它只支持作為 XSLT 處理程序的一部分導(dǎo)入的一組有限的命名空間。例如,您無(wú)法使用 System.Data 命名空間。腳本適合于一次性的操作,例如,分析字符串或檢索當(dāng)前時(shí)間。至于更為強(qiáng)大的替代解決方案,請(qǐng)考慮擴(kuò)展對(duì)象。
擴(kuò)展對(duì)象只是一個(gè)具 有一些公共方法的托管類(lèi)。唯一的要求是可調(diào)用的方法接受 XPath 類(lèi)型的參數(shù)或可以強(qiáng)制轉(zhuǎn)換到該類(lèi)型的參數(shù)。與嵌入式腳本(它們被自然地定義在樣式表的體中)不同,擴(kuò)展對(duì)象是必須以某種方式插入到樣式表中的外部資源。擴(kuò) 展對(duì)象參數(shù)必須按照以下代碼所示進(jìn)行傳遞:
ExtensionObject o = new ExtensionObject();// *** set properties on the object if needed
XsltArgumentList args = new XsltArgumentList();
args.AddExtensionObject("urn:dino-objects", o);
XslTransform xslt = new XslTransform();
xslt.Transform(doc, args, writer);
在 XSLT 腳本中,可以像使用嵌入式腳本那樣引用擴(kuò)展對(duì)象上的方法。在下面的代碼片段中,DoSomething 是與帶有“dino”前綴的命名空間相關(guān)聯(lián)的擴(kuò)展對(duì)象上的方法:
<xsl:template match="lastname"><TD style="border:1px solid black">
<xsl:value-of select="dino:DoSomething(., ../firstname)" />
</TD>
</xsl:template>
擴(kuò)展對(duì)象的方法被作為靜態(tài)方法調(diào)用進(jìn)行處理,這意味著如果您具有多個(gè)帶有相同方法名稱的對(duì)象,則最好使用不同的命名空間。
使用擴(kuò)展對(duì)象比使用嵌入式腳本更為可取,這至少有兩個(gè)原因。首先,擴(kuò)展對(duì)象提供了好得多的代碼封裝,更不用提實(shí)現(xiàn)類(lèi)重用的可能性以及可以使用任何托管類(lèi)型這一事實(shí)。其次,最后可以得到能夠從更加無(wú)縫的代碼維護(hù)中受益的更緊湊的分層樣式表。
異步 XSLT
Transform 方法具有幾個(gè)能夠返回 XML 讀取器的重載:
XmlReader Transform(XPathNavigator input, XsltArgumentList args);XmlReader Transform(IXPathNavigable input, XsltArgumentList args);
這些重載的簽名和行為與其他重載稍有不同。首先,輸入文檔必須是 XPathNavigator 或 XPathDocument。更重要的是,這些方法不接受任何代表輸出流的參數(shù)。實(shí)際上,轉(zhuǎn)換過(guò)程的輸出不是寫(xiě)出到流中,而是在流中創(chuàng)建并且通過(guò) XML 讀取器返回給用戶。
.NET Framework 中的整個(gè) XSLT 過(guò)程通過(guò)創(chuàng)建一個(gè)中間數(shù)據(jù)結(jié)構(gòu)(輸入導(dǎo)航器)工作,在該數(shù)據(jù)結(jié)構(gòu)中,樣式表的內(nèi)容被用作底層的基礎(chǔ)。在該樣式表源中發(fā)現(xiàn)的任何 <xsl> 標(biāo)記都被替換為展開(kāi)的文本或產(chǎn)生自嵌入式模板的任何調(diào)用序列。最終輸出看起來(lái)像是已編譯的程序,其中直接語(yǔ)句與子例程調(diào)用交替出現(xiàn)。這些語(yǔ)句被稱為輸出記 錄,而模板則充當(dāng)子例程的角色。當(dāng) Transform 方法獲得可以寫(xiě)入的輸出流時(shí),XSLT 處理程序遍歷所有記錄并將文本刷新到流中。如果已經(jīng)請(qǐng)求 XML 讀取器,則處理程序會(huì)創(chuàng)建某個(gè)內(nèi)部讀取器類(lèi)的實(shí)例,并將其返回給調(diào)用方。在調(diào)用方顯式請(qǐng)求讀取緩存的輸出記錄之前,不會(huì)執(zhí)行任何轉(zhuǎn)換(參見(jiàn)圖 12)。
圖 12 XSL 轉(zhuǎn)換過(guò)程
當(dāng) Transform 方法返回時(shí),讀取器處于其初始狀態(tài),這意味著它尚未針對(duì)讀取操作進(jìn)行初始化。每當(dāng)您從讀取器中彈出一個(gè)元素時(shí),都將正確地展開(kāi)并返回一個(gè)新的輸出記錄。這 樣,您可以完全控制轉(zhuǎn)換過(guò)程,并且可以實(shí)現(xiàn)許多奇特的功能。例如,您可以向用戶提供反饋,基于運(yùn)行庫(kù)條件和用戶角色丟棄節(jié)點(diǎn),或者使該過(guò)程在輔助線程上異 步發(fā)生。有關(guān)異步轉(zhuǎn)換的示例,請(qǐng)參見(jiàn)本文的代碼下載。
<asp:xml> 標(biāo)記
在結(jié)束本文之前,我希望考察 一個(gè)由 <asp:xml> 標(biāo)記標(biāo)識(shí)的特殊 ASP.NET 服務(wù)器控件。該控件是 XslTransform 類(lèi)的聲明性對(duì)應(yīng)物。可以使用 XML 服務(wù)器控件在 Web 頁(yè)中嵌入 XML 文檔。在需要可供客戶端使用的 XML 數(shù)據(jù)島時(shí),使用該控件很方便。數(shù)據(jù)島是 HTML 頁(yè)中引用或包含的 XML 數(shù)據(jù)。XML 數(shù)據(jù)可以用內(nèi)聯(lián)方式包含在 HTML 中,也可以存儲(chǔ)在外部文件中。通過(guò)將該控件的能力與執(zhí)行特定于瀏覽器的轉(zhuǎn)換的樣式表相結(jié)合,可以將服務(wù)器端 XML 數(shù)據(jù)轉(zhuǎn)換為不受瀏覽器影響的 HTML。例如,以下代碼嵌入指定的 XML 文件的內(nèi)容,就像由該頁(yè)中的樣式表進(jìn)行轉(zhuǎn)換一樣:
<div><asp:xml runat="server" id="xmldata"
documentsource="data.xml"
transformsource="ie5.xsl" />
</div>
XML 服務(wù)器控件可以通過(guò)編程方式進(jìn)行配置,并且通過(guò)字符串(DocumentContent 屬性)以及通過(guò) XmlDocument 對(duì)象(Document 屬性)接受源 XML。轉(zhuǎn)換元素可以通過(guò)指向 XSL 文件的 URL 或者通過(guò) XslTransform 類(lèi)的實(shí)例(Transform 屬性)提供。如果需要,還可以使用 TransformArgumentList 屬性指示參數(shù)。該組件與 HttpBrowserCapabilities 類(lèi)一起,為常見(jiàn)需要(數(shù)據(jù)驅(qū)動(dòng) Web 頁(yè)和特定于瀏覽器的輸出)提供了一種了不起的解決方案。
請(qǐng)注意,只有格式規(guī)范的 XML 數(shù)據(jù)才能與 <asp:xml> 控件一起使用。更簡(jiǎn)單地,如果需要刷新任何文件的內(nèi)容,則可以使用 Response.WriteFile 方法。
小結(jié)
在 本文中,我將 XPath 作為在托管應(yīng)用程序中執(zhí)行 XML 查詢的語(yǔ)言加以分析,并且討論了其實(shí)現(xiàn)的幾個(gè)方面。在 .NET Framework 中,XPath 運(yùn)行庫(kù)為其他令人困惑的部分(其中第一個(gè)是 XSLT)提供了公用基礎(chǔ)結(jié)構(gòu)。我還分析了 XSLT 處理程序的關(guān)鍵方面,并且提供了它的幾個(gè)有趣的應(yīng)用,例如異步處理和 ASP.NET 控件。
有關(guān)背景信息,請(qǐng)參閱:
Applied XML Programming for Microsoft .NET by Dino Esposito (Microsoft Press, 2002)
Essential XML Quick Reference: A Programmer's Reference to XML, XPath, XSLT, XML Schema, SOAP, and More by Aaron Skonnard and Martin Gudgin (Addison-Wesley, 2001)
XPathNavigator over Different Stores
MSDN Web Services Developer Center
轉(zhuǎn)載于:https://www.cnblogs.com/chenying99/archive/2011/03/10/1980209.html
總結(jié)
以上是生活随笔為你收集整理的通过 .NET Framework 中的 XPath 和 XSLT API 方便地操作 XML 数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: XP经典壁纸,多少人曾爱慕你年轻时的容颜
- 下一篇: 软件架构设计原则--开闭原则