高級 XML 驗證
XSLT 樣式表被設計用來轉換 XML 文檔。當基于語法的驗證不能覆蓋所有需要的約束時,通過與 Java 擴展一起使用,樣式表可以成為 XML Schema 的一個強有力的補充。在本文中,Peter Heneback 講解了使用 XSLT 和 Java 擴展來驗證文檔的案例,并提供了實用的指導
XSLT 樣式表被設計用來轉換 XML 文檔。當基于語法的驗證不能覆蓋所有需要的約束時,通過與
Java 擴展一起使用,樣式表可以成為 XML Schema 的一個強有力的補充。在本文中,Peter Heneback 講解了使用 XSLT 和
Java 擴展來驗證文檔的案例,并提供了實用的指導和代碼示例。
背景
基于語法的驗證語言,例如 XML Schema 和 DTD,可以很好地確保 XML 文檔遵從定義良好的消息結構。這樣可確保接收 XML 消息的應用程序能夠正確地處理接收到的 XML 消息,但是不能保證包含在消息中的數據是有效的。例如,基于語法的驗證語言的這些局限性意味著必須使用不同的方法來驗證變量和外部數據集上的同現約束(co-oclearcase/" target="_blank" >ccurrence constraints)和其他約束。
在很多情況下,用 XML Schema 或 DTD 不能實現的驗證邏輯被并入到應用程序代碼中。這種解決方案比較容易實現,但是得到的實現常常不夠靈活。本文首先調查研究 Schematron 這種解決上述問題的方法,接著指出這種方法的一些缺點。然后,本文探索一種使用 W3C 標準組件并結合 Java 擴展和開放源代碼 XSLT 處理器的方法。


|
回頁首 |
|
Schematron
常被推薦的用于補充 XML Schema 的方案是使用 Schematron。Schematron 是基于規則的語言,使用 XPath 來表達關于 XML 實例文檔中的內容的斷言。這是通過用一個基本樣式表轉換 Schematron 模式,將該模式轉換成一個 XSLT 樣式表來完成的。轉換的結果是一個 XML 格式的報告,其中包含關于哪些斷言失敗的詳細信息,并附有注釋。然而,Schematron 不大適用于定義結構,因為用 Schematron 定義結構很快就會變得吃力起來。因此,仍然需要首先用 XML Schema 對文檔進行驗證,但是 XML Schema 和 Schematron 一起可以滿足大多數應用程序的驗證需求。實際上,在 XML Schema 語法中,由于 Schematron 斷言處在不同的名稱空間里,兩者通??梢园ㄔ谕粋€文件中,并且分別作為文檔驗證過程的一部分。
圖 1 描繪了一個很常見的應用場景的邏輯處理步驟。XSLT 首先驗證傳入的 XML 實例文檔,然后在應用程序本身對它進行處理或者它被發送到外部應用程序之前對它進行轉換。從圖中很容易看出,當結合使用 XML Schema 和 Schematron 第一次執行驗證,然后使用一個 XSLT 樣式表轉換驗證過的文檔時,這一操作變得復雜起來??梢酝ㄟ^將模式分離,提前轉換 Schematron 模式來縮短該過程,但是這仍然需要運行兩次 XSLT 處理器,并且需要解析和檢查由 Schematron 轉換產生的驗證報告。
圖 1. 使用 Schematron 進行驗證
Schematron 的缺點
- 在使用 Schematron 模式驗證 XML 文檔之前,至少需要使用基本樣式表對該模式進行一次轉換。如果 Schematron 語法包括在 XML Schema 中,那么還需要進一步的轉換。
- Schematron 目前不能直接將報告返回給應用程序。在進行驗證之后,需要手動地或自動地對報告加以處理。


|
回頁首 |
|
XSLT 和 Java 擴展
在本節中,我將介紹 XSLT 加 Java 擴展這種補充基于語法的 XML Schema 驗證的方法,這是一種基于規則的方法。我將帶您親歷一組簡單的示例。至于如何使用 XML Schema 實現 XML 驗證器(validator),本文不作介紹,因為在 developerWorks 上的很多文章和教程上對此作了大量的闡述(參見 參考資料)。
圖 2 展示了與 圖 1 相同的場景,不過這次使用 XSLT 加 Java 擴展在一步內執行驗證和轉換。這一次不再產生一個報告,然后又必須單獨處理該報告,而是直接將驗證失敗以一個 ValidationException
對象的形式返回給應用程序,其中 ValidationException
類擴展了 Exception
類。除了減少文檔經過 XSLT 處理器的次數以外,在第一次驗證失敗時就會停止轉換,從而防止對無效數據的不必要的處理。
圖 2. 使用 XSLT 和 Java 擴展進行驗證
一個 XML 文檔的簡單轉換
為了演示如何使用 XSLT 和 Java 進行驗證,我使用一個簡單的登記表的轉換作為示例輸入,該登記表包含雇員詳細信息,例如姓名、電話號碼、稱呼和性別。稍后可以看到,稱呼和性別信息在同現約束的中央。清單 1 包含 XML 輸入文檔的一部分。
清單 1. XML 輸入文檔
<input:staff xmlns:input="cross-field-validation-namespace">
...
<input:employee id="1234A">
<input:first_name>Julia</input:first_name>
<input:last_name>Smith</input:last_name>
<input:title>Mrs</input:title>
<input:gender>F</input:gender>
<input:telephones>
<input:mobile preferred="false">0770-555 1231</input:mobile>
<input:mobile preferred="true">0771-555 1232</input:mobile>
<input:home preferred="false">0207-555 1233</input:home>
</input:telephones>
</input:employee>
...
</input:staff>
|
這個例子中的轉換簡單地將雇員登記表中的姓和名合并在一起。該轉換還提取那些 preferred 屬性被設置為 true
的電話號碼,如 清單 2 所示。
清單 2. 雇員數據的簡單轉換
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
xmlns:input="cross-field-validation-namespace">
<xsl:template match="/input:staff">
<phones>
<xsl:apply-templates select="input:employee" />
</phones>
</xsl:template>
<xsl:template match="input:employee">
<employee>
<name>
<xsl:value-of select="concat(input:first_name,' ',input:last_name)" />
</name>
<tel>
<xsl:value-of select="input:telephones/*[@preferred = 'true']" />
</tel>
</employee>
</xsl:template>
</xsl:stylesheet>
|
清單 3 顯示了通過用 清單 2 中的 XSLT 轉換輸入文檔而得到的輸出文檔。
清單 3. 輸出文檔
<phones xmlns:input="cross-field-validation-namespace"
xmlns:exception="xfield.exception.ValidationExceptionThrower"
xmlns:xalan="http://xml.apache.org/xslt">
<employee>
<name>Julia Smith</name>
<tel>0771-555 1232</tel>
</employee>
<employee>
<name>John Smith</name>
<tel>0207-555 1236</tel>
</employee>
<employee>
<name>Jenny Smith</name>
<tel>0770-555 1237</tel>
</employee>
</phones>
|
實現簡單的同現約束驗證
為了通過 XSLT 處理器將驗證錯誤返回給應用程序,只需要兩個非常簡單的 Java 類。為此,必須創建 ValidationException
類和 ValidationExceptionThrower
類。ValidationException
類是標準 Java Exception
類的一個簡單的擴展,它使應用程序可以將驗證錯誤與處理器拋出的其他異常區分開來。當 ValidationExceptionThrower
類的 throwException
方法被調用時,該類簡單地拋出 ValidationException
。為此另外還需要一個類。當在 XSLT 中使用 Java 擴展時,不能使用常規的 Java throw
語法拋出異常。只能使用用于 XSLT 的 Java 擴展來創建對象和調用方法。清單 4 和 5 展示了 Java 擴展所需的兩個類的完整源代碼。
清單 4. ValidationException 類
package xfield.exception;
public class ValidationException extends Exception{
public ValidationException(String sMsg)
{
super(sMsg);
} // end constructor
} // end class
|
清單 5. ValidationExceptionThrower 類
package xfield.exception;
public class ValidationExceptionThrower {
public ValidationExceptionThrower()
{
// Emtpy
} // end constructor
public void throwException(String sMessage) throws Exception
{
throw new ValidationException(sMessage);
} // end throwException
} // end class
|
檢查同現約束
如前所述,本文使用稱呼和性別作為一個常見同現約束的例子。您需要檢查稱呼和性別元素是否匹配。例如,如果 title 被設為 Mr,那么 gender 應該被設置為表示男性的 M。但是,要驗證 title 和 gender 元素是否分別包含有效的值,最好的做法是使用 XML Schema 中的枚舉。
為了進行驗證,將轉換代碼放入到 <choose/>
子句的 <otherwise/>
塊中,并檢查 <when/>
塊中的 test
語句。如果 test 語句的值等于 false
,也就是說驗證成功,那么就會執行轉換代碼。如果 test 語句的值等于 true
,那么就會使用一個 Java 擴展在 ValidationExceptionThrower
類的一個實例上調用 throwException()
。注意,在 XSLT 的根標記中附加了一個帶前綴 exception
的名稱空間,其中包含類名和包名。然后這個前綴被像一個對象名那樣用來調用 throwException()
方法,調用時以一個錯誤字符串作為參數?;氐?ValidationExceptionThrower
類的 Java 代碼上來,很容易看出一個包含 XSLT 中的錯誤字符串的 ValidationException
是如何被拋出的。
為了檢查進一步的狀況,添加所需的 <when/>
語句,并添加適當的錯誤消息作為這些方法的參數。
清單 6. 基本轉換和同現約束驗證
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
xmlns:exception="xfield.exception.ValidationExceptionThrower"
xmlns:input="cross-field-validation-namespace">
...
<xsl:template match="input:employee">
<xsl:choose>
<xsl:when test="input:gender = 'M' and input:title != 'Mr'
or input:gender = 'F' and input:title = 'Mr'">
<xsl:value-of
select="exception:throwException('Gender and title do not match')"/>
</xsl:when>
<xsl:otherwise>
<employee>
<name>
<xsl:value-of select="concat(input:first_name,' ',input:last_name)"/>
</name>
<tel>
<xsl:value-of select="input:telephones/*[@preferred = 'true']"/>
</tel>
</employee>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
|
如 清單 7 所示,添加 XPath 表達式到異常消息將產生更詳細的對驗證失敗的描述,這樣大大方便了發現問題。
清單 7. 詳細的驗證失敗信息
<xsl:when test="input:gender = 'M' and input:title != 'Mr'
or input:gender = 'F' and input:title = 'Mr'">
<xsl:value-of select="exception:throwException(
concat('Gender and title do not match for employee ',
input:first_name,' ',input:last_name))" />
</xsl:when>
|
將驗證與轉換分離
如果需要將轉換和驗證邏輯分離開來,那么可以使用一個標準的 <include/>
指令來引用驗證檢查,并將它們與轉換代碼一起執行。清單 8 展示了轉換代碼,其中 <include/>
標記引用了文件 validate.xsl
。還應注意添加的對名為 validate
的模板的調用。這個調用應該與被包括的文件中包含條件檢查的模板的名稱相匹配,如 清單 9 所示,這一點很重要。
清單 8. 轉換邏輯摘錄
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
xmlns:input="cross-field-validation-namespace">
<xsl:include href="validate.xsl" />
<xsl:template match="/">
<xsl:call-template name="validate" />
<phones>
<xsl:apply-templates />
</phones>
</xsl:template>
<xsl:template match="input:staff/input:employee">
<employee>
<name>
<xsl:value-of
select="concat(input:first_name,' ',input:last_name)" />
</name>
<tel>
<xsl:value-of
select="input:telephones/*[@preferred = 'true']" />
</tel>
</employee>
</xsl:template>
</xsl:stylesheet>
|
清單 9. 驗證邏輯摘錄
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:xalan="http://xml.apache.org/xslt"
xmlns:exception="xfield.exception.ValidationExceptionThrower"
xmlns:input="cross-field-validation-namespace">
<xsl:template name="validate">
<xsl:for-each select="/input:staff/input:employee">
<xsl:if
test="input:gender = 'M' and input:title != 'Mr'
or input:gender = 'F' and input:title = 'Mr'">
<xsl:value-of
select="exception:throwException('Gender and title do not match')" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
|
對外部引用數據進行驗證
XML Schema 可以使用枚舉將傳入的數據與一組預先決定的可接受的值進行比較。但是,如果引用數據是變量,且本身包含關系,那么 XML Schema 就無法對它進行驗證。這個例子表明,使用 XSLT 和 Java 擴展的解決方案可以滿足這種場景,并且可以對變量和外部 XML 數據集進行驗證。注意,Schematron 也提供了這種功能,因為被轉換的模式使用 XSLT 來執行驗證。
清單 10. 引用數據
<roles>
...
<employee id="1234A" role="A"/>
<employee id="1234D" role="A"/>
<employee id="1234C" role="X"/>
<employee id="1234B" role="Z"/>
<employee id="1234X" role="Z"/>
...
</roles>
|
這個例子中的外部引用數據是一組雇員,這些雇員有相關聯的角色。檢查傳入的 XML 文檔中的所有雇員 ID 是否都在引用數據集中。為此,使用 document()
函數將一個外部 XML 文檔裝載到一個變量中。檢查是否至少有一個具有當前 ID 的雇員在 reference
變量中,以斷言該雇員號是有效的。
清單 11. 驗證外部引用數據
<xsl:variable name="reference" select="document('reference.xml')" />
<xsl:template name="validate">
<xsl:for-each select="/input:staff/input:employee">
<xsl:if test="input:gender = 'M' and input:title != 'Mr'
or input:gender = 'F' and input:title = 'Mr'">
<xsl:value-of
select="exception:throwException(concat('Gender and title do not match
for employee ',input:first_name,' ',input:last_name))" />
</xsl:if>
<xsl:variable name="current_id" select="@id" />
<xsl:if
test="count($reference/roles/employee[@id = $current_id]) = 0">
<xsl:value-of
select="exception:throwException(concat('Invalid employee ID: ',$current_id))" />
</xsl:if>
</xsl:for-each>
</xsl:template>
|


|
回頁首 |
|
結束語
總而言之,當處理同現約束,或者需要一個 XML 報告工具時,Schematron 是 XML Schema 的一個很好的補充。但是當性能更為重要時,尤其是當驗證之后要進行轉換時,XSLT 加 Java 擴展是一種更緊湊的解決方案。


|
回頁首 |
|
下載
描述 | 名字 | 大小 | 下載方法 |
Java and XSLT source code used in this article |
advanced_xml_validation.zip |
18KB |
HTTP |
原文轉自:http://www.anti-gravitydesign.com
- 評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
-
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97
|