引言
XML Catalog 實現了根據 XSD 實時校驗 XML 文件的功能。用戶不用編寫程序,通過少量的配置就可以在編輯 XML 文件的時候得到及時的反饋(需要在 XML 編輯器進行文件的編寫),實現了實時的校驗。
然而在實際應用中,由於環境的不同以及 XML 文件本身的不同,手動方式的配置並不能滿足需求。比如,作者在實踐中發現,在開發環境裡手動配置的 XML Catalog 是不能保存在運行環境的。而且,許多實際的 XML 的編寫方式並沒有采用標准的格式,這也給 XML Catalog 的使用帶來了很多的不便。
本文針對以上問題,通過例子來說明如何通過擴展 XML Catalog 來實現基於 xsd 對 XML 文件的自動化實時校驗。
XML Catalog 介紹
XML Catalog 是基於 OASIS XML Catalog specification 標准的實現,它提出了一些關於 XML 文件如何引用外部資源的控制。Eclipse 的 WTP 提供了 XML Catalog 的功能,實現利用 schema 對 xml 文件的實時校驗功能。XML Catalog 是由來自一個或者多個 catalog 條目文件的條目組成的 xml 文件,其保存了要校驗的 xml 文件以及該文件對應的 xsd 文件的映射,在運行時可以自動將它們關聯起來,從而實現對 XML 文件的校驗。
下面通過一個例子來說明 XML catalog 的相關概念。
XML Catalog 相關概念介紹
<?XML version="1.0"?>
<!DOCTYPE catalog
PUBLIC "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN"
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"> 1
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:XML:catalog"> 2
<group prefer="public" xml:base="file:///usr/share/XML/" > 3
<public
publicId="-//OASIS//DTD DocBook XML V4.5//EN" 4
uri="docbook45/docbookx.dtd"/>
<system
systemId="http://www.oasis-open.org/docbook/XML/4.5/docbookx.dtd" 5
uri="docbook45/docbookx.dtd"/>
<system
systemId="docbook4.5.dtd" 6
uri="docbook45/docbookx.dtd"/>
</group>
</catalog>
DOCTYPE 表示這個文件是 OASIS XML catalog 文件。如果沒有 Internet 連接,那麼,整個 DOCTYPE 的聲明需要刪除或者注釋掉。因為 catalog 處理器會嘗試從網絡去下載 catalog.dtd 文件,顯然在沒有網絡的環境下,處理器因為找不到文件會報錯。
catalog 元素包含了 catalog 的內容和 catalog 的命名空間標識。
group 元素是一個包裝元素,可以設置在這個組裡面包含的所有 catalog 條目的屬性。屬性 prefer="public" 指出 catalog resolver(解析器) 在使用 SYSTEM 標識符之前優先使用 PUBLIC 標識符。屬性 xml:base 表明,所有 URI 都是相對這個路徑。本例中,uri="docbook45/docbookx.dtd"/ 是個相對路徑,絕對路徑應該是 file:///usr/share/XML/ docbook45/docbookx.dtd "。
public 元素將 publicId 映射到 uri 的路徑。publicId 是資源的一個唯一標識,通常是該文件的命名空間 .
system 元素將 systemId 映射到 uri 的路徑。systemId 與 publicId 一樣,也是資源的唯一標識,通常是資源文件在文件系統的全路徑。
XML Catalog 原理
XML Catalog 提供了一種重新定位資源的機制,可以將 xml 引用的 artifacts,包括 URI 地址以及 namespace 名重新定位到另一個地址。通常這種機制被用來將遠程的引用資源重定位到本地或者 web。XML catalog 就是一個描述外部實體引用和本地緩存的相同實體的映射的文件。
在實際的開發生產中,xml 文件經常會引用外部的文件,這些文件通常通過 URI 表示,其中以 URL 應用最廣。但是如果是絕對的 URL, 那麼只有當你的網絡能夠訪問它時才能起作用,如果網絡出現問題,那麼將不能訪問。當是相對 URL 時,例如"../../xml/dtd/docbookx.XML",只有當你的文件系統和定義者一致的時候才能起作用。
一種解決辦法就是通過實體解析器(Entity Resolver)或者是 URI 解析器 (URI Resolver ),解析器可以通過檢查資源的 URI 來定位資源。 用戶通過配置 xml catalog, 手動的指定 xml 文件引用的 xsd 文件的本地地址,URI 解析器通過 xml catalog 裡面的映射,找到對應的 xsd, 最後 xml catalog 處理器通過解析器找到的 xsd 對 XML 進行校驗。
通俗點說,XML catalog 通過命名空間將 XML 文件及其對應的 xsd 文件聯系起來,並通過解析器定位 xsd 文件的位置,最後通過處理器進行校驗。
與 Javax.xml.validation 通過 xsd 對 xml 進行校驗的方法不同,xml catalog 可以通過 Namespace 來校驗所有引用這個 xsd 的 xml 文件,從而達到批量校驗的效果。例如,a.xml,b.xml,c.XML 都是由 d.xsd 校驗,那麼只要將 d.xsd 的命名空間配置好,通過該命名空間就可以校驗以上三個文件了。
手動方式配置 XML Catalog
手動配置 XML Catalog 比較簡單也比較容易理解,可以在 Eclipse 的開發環境中直接進行。選擇 File -> New -> Other -> Examples -> Editing and Validating XML files,便可以進行配置了。如下圖所示:
圖 1. 打開 XML Catalog 的配置界面
查看原圖(大圖)
從圖 1 中可以看到有兩種 XML catalog entity , 用戶定義的實體及插件的實體。用手動方式生成的配置屬於用戶配置的實體。
圖 2. 新建一個 XML Catalog Entry
查看原圖(大圖)
新建一個 XML Catalog Entry 中的 Location 指明了 xsd 文件的位置,在這裡我們選擇來源於工作空間。
圖 3. 從工作空間選擇 student.xsd 文件
選擇 OK 以後,XML Catalog Entry 就創建完成了。XML Catalog 會自動填寫其他兩個屬性,如下圖所示。
圖 4. 完整的 XML Catalog Entry
如圖所示,Location 標簽指定了 xsd 的位置,在這裡是工作空間的相對路徑;Key Type 標簽表明了在這裡我們使用的是命名空間的名字作為 xml 和 xsd 的關聯;Key 標簽的值是 XML 命名空間的值。
圖 5. 完整的 XML Catalog Entry
查看原圖(大圖)
從圖 5 中可以看出,新建的 XML Catalog Entry 已經在 User Specified EntrIEs 目錄下。圖 1 到 4 顯示了 XML Catalog 的手動配置步驟。下面,我們在 XML 編輯器裡打開一個 xml 文件,看一下 XML Catalog 的功能。
圖 6. 正確的 XML 文件
查看原圖(大圖)
首先,用 XML 編輯器打開 sample.XML 文件。
圖 7. XML Catalog 的實時校驗結果
將 name 為 BB 的學生的 age 標簽去調,從圖中可以看到在編輯器的右邊有一個紅色的點,同時在 student 元素下面有個紅色的波浪線,提示 student 元素有錯誤。這是,將鼠標移到 student 元素上,可以看到具體的提示信息,student 元素不完整,缺少 age 元素。
可以看出,應用 XML Catalog 對 XML 文件進行實時校驗,配置步驟比較簡單,只需提供相應的 xsd 文件及位置,就能將他們關聯起來,實現自動的校驗。
擴展 XML Catalog Contributions
在實踐中發現,如果通過上一節介紹的手動方式在 Eclipse 開發環境配置 xml catalog,那麼這些配置並不會在運行環境生效。然而通常情況下,我們需要的是運行環境的配置。比如我們要開發一些小工具,可以讓用戶編輯 xml 文件,同時我們內置了一些 xsd 文件來校驗用戶編輯的 xml 文件是不是正確。因為不能讓用戶在使用產品的時候再重新配置(因為對用戶來說,他們並不需要知道這些 xsd 的存在,還有就是,不能增加用戶的負擔)。所以,我們需要在開發環境配置好 xsd 的信息,並讓這些配置在運行時生效。通過調研,我們發現,如果要在運行環境中直接使用開發環境的配置,那麼需要擴展 XML catalog Contributions 這個擴展點。
下面我們就一步步的演示如何在 Eclipse 插件裡擴展 XML Catalog Contributions 擴展點。
1.打開 plugin.XML 文件的 Extensions 標簽,點擊 Add 按鈕,添加擴展點。
圖 8. 添加擴展點
查看原圖(大圖)
2.在 Extension Point filter 文本框裡輸入擴展點的名字,org.eclipse.wst.XML.core.catalogContributions,並選中該擴展點,點擊 OK 按鈕。
圖 9. 選中擴展點
圖 10 展示了成功添加擴展點後的 Extension 頁面。
圖 10. 成功添加擴展點
查看原圖(大圖)
3.新建一個 catalogContribution
圖 11. 新建 catalogContribution
查看原圖(大圖)
4.新建一個 public
圖 12. 新建 public
查看原圖(大圖)
5.填寫 public 的屬性信息。public 有兩個屬性,publicId 和 uri。
publicId 就是命名空間(namespace),uri 就是 xsd 文件的物理位置,可以通過 Browse 按鈕來選擇 xsd 文件。
圖 13. 填寫 public 詳細信息
查看原圖(大圖)
通過以上 5 步,就完成了對 xml catalog Contributions 的擴展。圖 14 就是完成以上配置以後的 plug.XML 文件內容。從這個文件我們可以清楚地看清楚擴展的相應配置。
圖 14. plugin.XML 文件
查看原圖(大圖)
從圖 15 可以看出,與手工方式的配置不同,在運行時的環境裡,User Specified EntrIEs 裡面並沒有內容。
圖 15. 運行環境的 XML Catalog 配置
查看原圖(大圖)
將 sample.xml 改錯,可以看見 XML Catalog 的提示信息。
圖 16. sampe.XML 文件
查看原圖(大圖)
通過以上的步驟,我們演示了,如何擴展 XML Catalog。在下一個部分,我們將會演示如何擴展 URI Resolver 實現特殊 XML 文件的校驗。
擴展 URI Resolver
XML Catalog 雖然提供了比較強大的功能,但是由於實際生產環境的復雜性,一些 xml 文件並不能由其進行校驗。比如,有些 xml 文件,由於書寫不規范,並沒有命名空間,還有些 xml 文件,由於應用環境的原因,其使用了 xsi:schemaLocation 元素,將引用的 xsd 文件直接定位到了虛擬的路徑,導致 xml catalog 的定位功能失效 . 對於第一種情況,由於 XML Catalog 的設計理念就是通過命名空間進行 xml 和 xsd 的關聯,所以,不支持此種情況。對於第二中情況,我們可以通過擴展 XML Catalog 來實現。
在擴展 URI Resolver 之前,我們對 XML Catalog 可以處理的 xml 和 xsd 的類型進行了分類,以便大家能夠清楚地知道 XML Catalog 可以解決哪些問題。
xsd \ XML 有命名空間 無命名空間 有命名空間,無 xsi:schemaLocation 可以 不可以 有命名空間,有 xsi:schemaLocation,且其值是 xsd 實際存在的位置 可以 不可以 有命名空間,有 xsi:schemaLocation,且其值不是 xsd 實際存在的位置 可以 不可以 無命名空間URI Resolver 簡介
URIResolver 負責資源的定位,XML Catalog 的處理器根據 URIResolver 的資源位置找到相應的 xsd,然後進行校驗。
特殊的 XML 文件
如圖 18 所示,sample.xml 文件裡中有一個 xsi:shemaLocation 屬性,它將 xsd 的位置定位到 http://www.sample.com/sample/schemas/student.xsd。正常情況下,XML Catalog 會到該位置去找 xsd, 然而,在本文中這是一個無效的地址,所以 XML Catalog 會因為找不到 xsd 而不起作用。
圖 18. 帶有 schemaLocation 標簽的 XML 文件
查看原圖(大圖)
擴展 org.eclipse.wst.common.uriresolver.resolverExtensions
resolverExtensions 擴展點可以讓用戶注冊自己的 URI Resolver,從而達到擴展默認 Resolver 的功能的作用。與默認的 Resolver 一樣,用戶擴展的 Resolver 也可以被編輯器,校驗器和向導調用。
圖 17. 添加擴展點 resolverExtensions
查看原圖(大圖)
如下圖所示,該擴展點有三個屬性,class(類名), stage(階段)和 priority(優先級)。Class 指定了實現 org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver 接口的類;stage 指定了在哪個階段運行 resolver(解析器),有三個值,分別是 prenormalization, postnormalization 和 physical,默認是 physical。Prenormalization 表示解析器在輸入參數的格式統一之前運行,postnormalization 表示解析器在輸入參數的格式統一之前運行,physical 表示在所有的 pre 和 postnormalization 解析器之後運行。Priority 指定了在處在相同 stage 的解析器的執行優先級。優先級分為 high(高級),medium(中級)和 low(低級)三種,默認為中級。
圖 18. 填寫擴展點的屬性
查看原圖(大圖)
下圖為增加了 resolverExtensions 擴展點的 plugin.XML 文件的內容。
圖 19. plugin.XML 文件內容
查看原圖(大圖)
實現 URIResolverExtension
1.首先,在插件中新建一個類,實現 URIResolverExtension 接口。URIResolverExtension 接口提供了 resolve 方法,用來重新定位 xsd 資源,也就是找到有效的 xsd。該方法的參數有四個,第一個類型為 IFile,是在工作空間 (workspace) 中的文件,第二個參數是 String baseloaction,它是該文件在文件系統的絕對路徑,第三個參數是 publicId,它是該文件的命名空間,最後一個參數是 String systemid,它是文件的實際路徑,對於 xml 文件來說,它的 systeid 為空。但是因為我們在第 2 小節擴展了 XML Catalog, 所以,對於在 XML Catalog 文件裡面配置好的 publicId,它的 systemId 就是該文件裡面 Uri 的值,也就是 xsd 文件的實際路徑。該函數的返回值就是 XML 文件引用的 xsd 文件的位置。
清單 1. 實現 URIResolverExtension 接口
public class MyURIResolverExtension implements URIResolverExtension {
public MyURIResolverExtension() {
}
@Override
public String resolve(IFile file, String baseLocation, String publicId,
String systemId) {
return null;
}
}
清單 2. 添加 catalog 到 catalog manager
ICatalog catalog = XMLCorePlugin.getDefault().getDefaultXMLCatalog();
if (catalog == null) {
return null;
}
清單 3. 重新定位資源
if (myResolved == null) {
if (publicId != null) {
if ((systemId != null && systemId.endsWith("student.xsd"))) //$NON-NLS-1$
{
try {
int index = systemId.lastIndexOf("/");
if (index > -1)
systemId = systemId.substring(index);
myResolved = catalog.resolvePublic(publicId, systemId);
} catch (MalformedURLException me) {
myResolved = null;
} catch (IOException IE) {
myResolved = null;
}
}
}
}
由清單 3 可以看出,我們通過 catalog manager 的 resolvePublic 方法,通過 publicId 找到 Catalog 裡與 publicId 對應 uri 的值,這個值就是 xsd 文件的路徑。
小結
本文首先介紹了 XML Catalog 相關概念以及基本原理,使讀者對其有了初步的認識。接著通過一個簡單的例子介紹了手動方式配置 XML Catalog 的步驟,使讀者有了進一步的了解。最後,通過擴展 XML Catalog 和 URI Resolver 兩個擴展點來實現比較高級的功能,使讀者對該技術有了深入的了解。XML Catalog 是比較通用的解決 XML 實時校驗問題的方法,尤其與 XML Catalog 和 URI Resolver 兩個擴展點的結合在解決一些特殊的 XML 文件的實時校驗問題時,往往能取得事半功倍的效果。