top
Loading...
XML介紹系列篇十
XML介紹系列之十八---如何使用XSL和正則表達式來驗證數據的有效性(二)

翻譯:Batman
系列之十八:如何使用XSL和正則表達式來驗證數據的有效性(二)
現在繼續我在上一系列中介紹的例子。我們將實現一個簡單的搜索機制,
也許你的興趣不是在獲取書籍的標題而是在要獲得整個書節點(note)上。
那么使用下面這個函數就能夠滿足你的要求了
Public Function GetFilteredElements(activeElement As Variant, RegExpfilter As String, _
Optional queryString As String = "",optional IsGlobal as Boolean=True, _
optional IgnoreCase as Boolean = True) As IXMLDOMNodeList
Dim re As RegExp
Dim filterDoc As DOMDocument
Dim nodelist As IXMLDOMNodeList
Dim filterList As IXMLDOMNodeList
Dim node As IXMLDOMElement
Dim baseNode As IXMLDOMElement
Set re = New RegExp
On Error GoTo ErrorHandler
Select Case TypeName(activeElement)
Case "IXMLDOMElement"
Set baseNode = activeElement
Case "DOMDocument"
Set baseNode = activeElement.documentElement
Case Else
Error 1001
Set GetFilteredElements = Nothing
End Select
re.Pattern = RegExpfilter
re.Global=IsGlobal
re.IgnoreCase=IgnoreCase
If queryString = "" Then
Set filterList = baseNode.selectNodes(".[textnode()]|.//*[textnode()]")
Else
Set filterList = baseNode.selectNodes(queryString)
End If
For Each node In filterList
If re.Test(node.Text) Then
node.setAttribute "filter:filteredElementFound", "true"
End If
Next
Set filterList = baseNode.selectNodes(".[@filter:filteredElementFound]|.//*[@filter:filteredElementFound]")
For Each node In filterList
node.removeAttribute "filter:filteredElementFound"
Next
Set GetFilteredElements = filterList
Exit Function
ErrorHandler:
If Err.Number = 1001 Then
MsgBox "Document must be an XML document, or a document element."
Else
Error Err.Number
End If
End Function

下面是對這個方法的簡單描述:
GetFilteredElements主要是用來獲取一個XML的文檔或則一個文檔里面的節點,并且
按照我們的需要轉換它(包括所有的該節點的子節點),將轉換后的節點放到一個
列表類型IXMLDOMNodeList中.
這個過濾器程序按次序對每個節點進行運用,如果一個節點的文本滿足了表達式的話,
那么這個節點就被做上一個臨時屬性的標記為filter:filteredElementFound
(當然為了不和你自己的XML文檔里已經存在的標志發生沖突,你可以把這個屬性
改成你需要的東西).
一旦所有的節點都被檢查完畢后,一個新的列表對象(僅僅只包含那些滿足表達式的
節點)被創建了。這些臨時的屬性會被刪除掉,然后返回那些節點。

如果你沒有給這個函數輸入查詢參數的話,
那么將獲得整個文檔的所有葉子節點(就是那些沒有子元素的節點)
如果你給這個函數輸入查詢參數的話,
那么這個函數將返回滿足條件的節點或則子節點
例如,下面的代碼將返回第一本書的標題和描述的節點和第四本書的
描述節點
Dim bookXML=new DOMDocument
bookXML.load("bookCatalog.xml")
Set nodelist=GetfilteredElements(bookXML,"xml")
而下面的代碼將返回第一和第四本書的節點,然后你可以根據這些節點獲取它們的
子節點的屬性。
Set nodelist=GetfilteredElements(bookXML,"xml","//book")
通常,你應該盡可能的定義一個XSL的查詢過濾器(filter)
因為它會只返回你需要的節點,會大大減少你需要處理的數據量。

這個函數是XML "數據庫"的一個運用實例,因為很多SQL的開發者用熟悉了的
參數(例如LIKE)在XML中是沒有等效的方法的,但是只要你在XML中能夠熟練使用
正則表達式,你會發現它能夠實現很多LIKE語句能夠實現的功能。

使用XSL轉換驗證數據
正則表達式使用在XSL的轉換中是一個強有力的驗證數據有效的工具。
例如,你想根據XML的數據生成一個顯示書籍標題和描述的table
當你使用基于DOM版本(就是使用微軟的那個模型XDOM)的時候,XSL已經能夠實現非常復雜的轉換XML為
HTML的功能了。
在XSL的這個節點中<xsl:script>,允許你使用腳本語言。
你可以在輸出流中插入文本(但是目前你就不能夠把一個DOM節點輸出到輸出流中).
XSL中默認的腳本語言是JavaScript,
在使用的過程中你需要注意的是,由于"/"在JavaScript中是特殊字符,你需要使用
"//"將其轉意。
代碼如下:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:script language="JavaScript"><![CDATA[
IsValidBookTopic=/xml/
]]></xsl:script>
<xsl:template match="/">
<xsl:apply-templates select="//book" />
</xsl:template>
<xsl:template match="book">
<xsl:if expr="IsValidBookTopic.test(this.text)">
<h1><xsl:value-of select="title"/></h1>
<h2><xsl:value-of select="author"/></h2>
<p><xsl:value-of select="description"/></p>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
當然,你也可以改變你的XSL中的查詢參數(例如上面例子中使用的參數是xml)
一個直接的方法是生成一個參數實體"entities"
例如下面的代碼,你需要使用"%"字符來說明一個字符是參數。
Function SetXSLParameter(XslDoc as DOMDocument ,ParamName as _
String,ParamValue as Variant) as DOMDocument
Dim XslDoc as DOMDocument
Dim ScriptNode as IXMLDOMElement
Dim re as RegExp

Set re=new RegExp
Re.global=True
Re.IgnoreCase=True
Re.pattern="%"+ParamName
For each ScriptNode in xslDoc.selectNodes("xsl:script")
ScriptNode.text=Re.replace(ScriptNode.text,cstr(ParamValue))
Next
Return XslDoc
End Function

SetXSLParameter用來給XSL文檔設置參數。
當然你也可以不使用上面這個函數,直接在XSL的腳本里面修改這個參數,
但是如果你使用了上面這個函數的話,可以讓你的代碼的可擴展性能更好。
注意的是,XSL的代碼將被上面這個函數所修改
所以如果你想對相同的XSL對象使用不同的過濾器(就是不同的查詢條件的話)
需要使用XSL對象的clone(就是制作一個XSL的副本)方法。
下面就是一個完整的例子
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:script language="JavaScript"><![CDATA[
IsValidBookTopic=/%searchStr/
]]></xsl:script>
<xsl:template match="/">
<xsl:apply-templates select="//book" />
</xsl:template>
<xsl:template match="book">
<xsl:if expr="IsValidBookTopic.test(this.text)">
<h1><xsl:value-of select="title"/></h1>
<h2><xsl:value-of select="author"/></h2>
<p><xsl:value-of select="description"/></p>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

然后你就可以使用下面這段代碼來修改這個參數了
呵呵,我是搞asp的,下面這段代碼就是寫在你的asp的程序中的。

Set xmlDoc=new DomDocument
Set xslDoc=new DOMDocument
XmlDoc.load "catalog.xml"
XslDoc.load "catalog.xsl"
SetXSLParameter xslDoc, "searchStr", "pair-o-dice"
Response.write xmlDoc.transformNode(xslDoc)

同樣,使用上面的方法,你就可以設置其他的參數了,例如瀏覽器的類型呀
ASP的查詢參數呀,或則一個基于Web的組件呀。

使用正則表達式給XSL帶來了強大的功能。
雖然,使用這種通用性很強的方法在目前也許你不會看到能夠給你帶來很
大的優勢(主要是很少有人寫XML的程序的說),但是在未來的編程過程中,
你將會體會到它的強大威力了。