XML 命名空間以及它們如何影響 XPath 和 XSLT (Extreme XML)

原文地址:https://msdn.microsoft.com/zh-cn/library/ms950779.aspx#EFAA

發佈日期 : 2004-4-3
更新日期 : 2004-4-30

Dare Obasanjo

Microsoft Corporation

2002 年 5 月 20 日

本文是有望長期發表的系列文章的第一篇,這些文章專門闡釋由 Microsoft 支持的 W3C XML 技術的更微妙的內容。儘管 XML 的核心仍相當簡單,但是圍繞它的技術已經變得日益複雜,而且其中的一些技術需要相當多的專業知識才能掌握。本文以及隨後的其他文章旨在將各種 W3C XML 建議中的信息提取爲便於理解的信息,供 XML 用戶和開發人員使用。

在這一系列文章中,第一篇是有關 XML— 命名空間常被誤解的方面。XML 命名空間是 W3C 的大部分 XML 建議和工作草案(包括 XPath、XML 架構、XSLT、XQuery、SOAP、RDF、DOM 和 XHTML)中不可缺少的一方面。對於任何使用 XML 的人來說,瞭解命名空間如何工作以及它們如何與依賴它們的許多其他 W3C 技術進行交互非常重要。

本文介紹 XML 命名空間的詳細內容,以及它們在支持命名空間的許多 XML 技術上的分支。

*
本頁內容

XML 命名空間概述 
XPath、XSLT 和命名空間 
XML 命名空間警告 
命名空間的未來 

XML 命名空間概述

隨着 XML 在 Internet 上的使用日益廣泛,能夠創建可組合和重用的標記詞彙表(方法類似於軟件模塊的組合和重用)這一優勢變得日益重要。如果已經存在一個定義完善的標記詞彙表,用於描述硬幣集合、程序配置文件或快餐店的菜單,則重用它會比從頭設計一個更有意義。將多個現有的詞彙表組合在一起,以便創建“事物的整體比它各個部分的總和還大”的新詞彙表,也成爲 XML 用戶開始需要的一個功能。

但是,在同一個文檔中,來自不同詞彙表的同一個標記(特別是 XML 元素和屬性)可能具有不同的語義,這最終會產生問題。XML 的高度擴展性以及它在 Internet 上的廣泛應用排除了只是將保留的元素或屬性名稱指定爲此問題的解決方案。

W3C XML命名空間建議旨在創建一個機制,以便 XML 文檔中來自不同標記詞彙表的元素和屬性可以被明確標識和組合,而無需處理所產生的問題。XML 命名空間建議提供了一種方法,以便基於處理要求對 XML 文檔中的各個項目進行分區,而無需針對應當如何命名這些項目設置過多的限制。例如,名爲

 <template>
<output>
<stylesheet>
的元素可以出現在 XSLT 樣式表中,而對於它們是轉換指令還是轉換的可能輸出沒有二義性。

XML 命名空間是一組由 統一資源標識符 (URI) 引用標識的名稱,這些名稱在 XML 文檔中用作元素名稱和屬性名稱。

命名空間聲明

ms950779.xml05202002-fig01(zh-cn,MSDN.10).gif

圖 1. 利用命名空間的 XML 代碼片段

命名空間聲明通常用於將命名空間 URI 映射到特定的前綴。前綴-命名空間映射的作用域包括命名空間聲明作用的元素及其所有的子級。前綴爲 xmlns: 的屬性聲明是命名空間聲明。類似屬性聲明的值應當是作爲命名空間名稱的命名空間 URI。

在下面的示例 XML 文檔中,根元素包含一個將前綴 bk 映射到命名空間名稱

urn:xmlns:25hoursaday-com:bookstore
的命名空間聲明,它的子元素包含一個
inventory
元素,inventory 元素中包含一個將前綴
inv
映射到命名空間名稱
urn:xmlns:25hoursaday-com:inventory-tracking
的命名空間聲明。
<bk:bookstore xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">
 <bk:book> 
    <bk:title>Lord of the Rings</bk:title> 
    <bk:author>J.R.R. Tolkien</bk:author>
    <inv:inventory status="in-stock" isbn="0345340426" 
        xmlns:inv="urn:xmlns:25hoursaday-com:inventory-tracking" />
 </bk:book> 
</bk:bookstore>

在上例中,

urn:xmlns:25hoursaday-com:bookstore
命名空間名稱的命名空間聲明作用域是整個
bk:bookstore
元素,而
urn:xmlns:25hoursaday-com:inventory-tracking
的命名空間聲明作用域是
inv:inventory
元素。能夠識別命名空間的處理器可以獨立處理來自這兩個命名空間的項目,這會使其能夠對 XML 文檔執行多層處理。例如,RDDL 文檔是有效的 XHTML 文檔,這些文檔不僅可以由 Web 瀏覽器呈現,而且還包含使用
http://www.rddl.org
命名空間中元素的信息,該命名空間可用於查找有關 XML 命名空間成員的機讀資源。

應當注意的是,按照定義,前綴 xml 綁定到 XML 命名空間名稱,而且這個特殊的命名空間自動在每個格式規範的 XML 文檔中對文檔作用域預先進行聲明。

默認命名空間

有關命名空間聲明的上一節不是完整的,因爲它未考慮默認命名空間。默認命名空間聲明是一個屬性聲明,該屬性聲明的名稱是

xmlns
,其值是作爲命名空間名稱的命名空間 URI。

默認命名空間聲明指定其作用域中所有不帶前綴的元素名稱都來自聲明的命名空間。下面的書店示例使用默認命名空間,而不使用前綴-命名空間映射。

<bookstore xmlns="urn:xmlns:25hoursaday-com:bookstore">
 <book> 
    <title>Lord of the Rings</title> 
    <author>J.R.R. Tolkien</author>
    <inv:inventory status="in-stock" isbn="0345340426" 
        xmlns:inv="urn:xmlns:25hoursaday-com:inventory-tracking" />
 </book> 
</bookstore>

在上例中,除

 inv:inventory
元素以外的所有元素都屬於
urn:xmlns:25hoursaday-com:bookstore
命名空間。默認命名空間的主要目的是縮短使用命名空間的 XML 文檔。但是,如果對於元素名稱使用默認命名空間,而不使用顯式映射的前綴,可能會導致混淆,因爲文檔中的元素不是明顯地屬於命名空間的作用域。

此外,與常規的命名空間聲明不同的是,默認命名空間聲明可以通過將 xmlns 屬性的值設置爲空字符串來取消聲明。應當避免取消對默認命名空間聲明的聲明,因爲這一做法可能導致在文檔的一部分中,具有屬於某個命名空間且不帶前綴的名稱,但是在另一部分中卻沒有。例如,在下面的文檔中,只有

bookstore
元素來自
urn:xmlns:25hoursaday-com:bookstore
,而其他不帶前綴的元素沒有命名空間名稱。
<bookstore xmlns="urn:xmlns:25hoursaday-com:bookstore">
 <book xmlns=""> 
    <title>Lord of the Rings</title> 
    <author>J.R.R. Tolkien</author>
    <inv:inventory status="in-stock" isbn="0345340426" 
        xmlns:inv="urn:xmlns:25hoursaday-com:inventory-tracking" />
 </book> 
</bookstore>

應當避免這一做法,因爲它對於 XML 文檔的讀者會產生非常容易混淆的情況。有關對命名空間聲明取消聲明的詳細信息,請參閱“命名空間的未來”一節。

限定名稱和擴展名稱

限定名稱又稱爲 QName,是一個名爲本地名稱的 XML 名稱,它的前面可以有另一個名爲前綴的 XML 名稱和一個冒號 (':')字符。用作前綴的 XML 名稱和本地名稱必須與產生的 NCName 相匹配,這意味着它們不得包含冒號字符。限定名稱的前綴必須已經通過作用域內的命名空間聲明(將前綴映射到命名空間 URI)映射到命名空間 URI。限定名稱可以用作屬性名稱,也可以用作元素名稱。

儘管 QName 是重要的助記指導,可以幫助確定文檔中的元素和屬性是從哪個命名空間派生的,但是它們對於能夠識別 XML 的處理器無關緊要。例如,下面的三個 XML 文檔將被一系列 XML 技術(當然包括 XML 架構驗證程序)以同樣的方式處理。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:complexType id="123" name="fooType"/>
</xs:schema>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:complexType id="123" name="fooType"/>
</xsd:schema>

<schema xmlns="http://www.w3.org/2001/XMLSchema">
        <complexType id="123" name="fooType"/>
</schema>

W3C XML 路徑語言建議擴展名稱描述爲命名空間名稱和本地名稱對。通用名稱是由 James Clark 創造的一個替換術語,可用來描述同一個概念。通用名稱由命名空間名稱(用大括號括起來)和本地名稱組成。從通用名稱的角度看,命名空間對於人們的意義更大。下面是上面示例中的三個 XML 文檔,但是 QName 被替換爲通用名稱。請注意,下面的語法不是有效的 XML 語法。

<{http://www.w3.org/2001/XMLSchema}schema>  
<{http://www.w3.org/2001/XMLSchema}complexType id="123" name="fooType"/>
</{http://www.w3.org/2001/XMLSchema}schema>

<{http://www.w3.org/2001/XMLSchema}schema>  
<{http://www.w3.org/2001/XMLSchema}complexType id="123" name="fooType"/>
</{http://www.w3.org/2001/XMLSchema}schema>

<{http://www.w3.org/2001/XMLSchema}schema>  
<{http://www.w3.org/2001/XMLSchema}complexType id="123" name="fooType"/>
</{http://www.w3.org/2001/XMLSchema}schema>

對許多 XML 應用程序來說,XML 文檔中元素和屬性的通用名稱非常重要,而用在特定 QName 中的前綴的值並不重要。XML 中的命名空間建議之所以沒有使用擴展名稱方法來指定命名空間,主要是因爲它很冗長。相反,之所以提供前綴映射和默認命名空間,是爲了避免我們因不斷鍵入命名空間 URI 而產生腕管綜合症。

命名空間和屬性

除非屬性的名稱有前綴,否則命名空間聲明不應用於屬性。在下面顯示的 XML 文檔中,

title
屬性屬於
bk:book
元素且沒有命名空間,而
bk:title
屬性將
urn:xmlns:25hoursaday-com:bookstore
作爲其命名空間名稱。請注意,即使這兩個屬性具有相同的本地名稱,該文檔的結構也是完整的。
<bk:bookstore xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">
   <bk:book title="Lord of the Rings, Book 3" bk:title="Return of the King"/> 
</bk:bookstore> 

在下例中,即使指定了一個默認命名空間,

title
屬性仍然沒有命名空間,且屬於
book
元素。換句話說,屬性不能繼承默認命名空間。
<bookstore xmlns="urn:xmlns:25hoursaday-com:bookstore">
   <book title="Lord of the Rings, Book 3" /> 
</bookstore> 

命名空間 URI

按照 RFC 2396 中的規定,命名空間名稱是統一資源標識符 (URI)。URI 是統一資源定位器 (URL) 或統一資源名稱 (URN)。URL 用於指定資源在 Internet 上的位置,而 URN 應該是信息資源持久的、獨立於位置的標識符。對於不同的命名空間名稱來說,只有當它們的每個字符(區分大小寫)都相同時,才被視爲相同。之所以將 URI 用作命名空間名稱,主要是因爲它們已經提供了一種用來指定全局唯一標識符的機制。

XML 命名空間建議聲明,命名空間名稱只是充當唯一標識符,而不必實際標識可從網絡檢索的資源。這就在 XML 文檔的作者和用戶之間產生了很大的混淆,特別是由於人們已經普遍將基於 HTTP 的 URL 用作命名空間名稱。因爲許多應用程序都將這樣的 URI 轉換爲超級鏈接,但是當這些“鏈接”無法跳轉到網頁或其他可從網絡檢索的資源時,會令許多用戶感到惱火。我記得有一個用戶將這比作在社交場合中得到了一個假電話號碼。

爲了避免使用戶感到混淆,一個解決方案就是使用不暗示資源具有網絡可檢索性的命名空間-命名架構。在編寫供個人使用的 XML 文檔時,我使用

urn:xmlns:
方案來實現此目的,並創建了一個類似於
urn:xmlns:25hoursaday-com
的命名空間名稱。自建命名空間 URI 有一個問題,那就是它們可能會由於全局不唯一而與 XML 中的名稱建議相抵觸。我通過將我的個人域名 http://www.25hoursaday.com 用作命名空間 URI 的一部分來滿足這個全局唯一要求。

另一個解決方案是將可從網絡檢索的資源保留在作爲命名空間名稱的 URI(如用 XSLT 和 RDDL 命名空間實現的 URI)處。通常,這樣的 URI 實際上是 HTTP URL。通過使用 W3C 所建議的格式,可以很好地對這樣的 URL 進行命名,如下所示:

      http://my.domain.example.org/product/[year/month][/area]

有關將結構類似的命名空間名稱用作版本控制機制的詳細信息,請參閱“命名空間和版本控制”一節。

有關命名空間的 DOM、XPath 和 XML 信息集

W3C 已經定義了許多可以爲 XML 文檔提供數據模型的技術。這些數據模型通常是一致的,但是由於歷史原因,它們在處理各種邊緣案例的方式上有時會有所不同。對 XML 命名空間和命名空間聲明的處理就是邊緣案例的一個示例,在 W3C 建議中的三個主要數據模型中,該案例會以不同的方式進行處理。這三個數據模型是 XPath 數據模型、文檔對象模型 (DOM) 和 XML 信息集。

XML 信息集 (XML infoset) 是對 XML 文檔中數據的抽象說明,它可視爲 XML 文檔的主要數據模型。XPath 數據模型是類似於 XML 信息集的基於樹的模型,在查詢 XML 文檔時會遍歷該模型。DOM 優先於 XPath 和 XML 信息集這兩種數據模型,但是它在許多方面也與這兩個數據模型相似。DOM 和 XPath 數據模型可視爲對 XML信息集的解釋。

文檔對象模型 (DOM) 中的命名空間

DOM Level 3 規範的 XML 命名空間部分將命名空間聲明視爲以 http://www.w3.org/2000/xmlns/ 作爲其命名空間名稱的常規屬性節點,並將 xmlns 視爲它們的前綴或限定名稱。

DOM 中的元素和屬性有一個無法在創建之後進行修改的命名空間名稱,不管它們在文檔中的位置是否發生改變。

XPath 數據模型中的命名空間

W3C XPath 建議不將命名空間聲明視爲屬性節點,而且不提供以該資格訪問它們的權限。相反,在 XPath 中,XML 文檔中的每個元素都有許多可使用 XPath 命名空間導航軸檢索的命名空間節點

在文檔中每個元素的作用域內,該元素對於每個命名空間聲明都有一組唯一的命名空間節點。在該命名空間中,命名空間節點對於每個元素都是唯一的。因此,代表同一個命名空間聲明的兩個不同元素的命名空間節點是不同的。

XML 信息集中的命名空間

XML 信息集建議將命名空間聲明視爲屬性信息項

另外,與 XPath 數據模型相似的是,XML 文檔信息集內的每個元素信息項,對於該元素作用域內每個命名空間都有一個命名空間信息項目。

XPath、XSLT 和命名空間

W3C XML 路徑語言(也稱爲 XPath)用於對 XML 文檔的某些部分進行尋址,它可用在許多 W3C XML 技術(包括 XSLT、XPointer、XML 架構和 DOM Level 3)中。XPath 使用類似於文件系統和 URL 中使用的分層尋址機制來檢索 XML 文檔的某些部分。XPath 支持對字符串、數字和布爾值進行基本操作。

XPath 和命名空間

XPath 數據模型將 XML 文檔視爲節點(如元素、屬性和文本節點)樹。在節點樹中,每個節點的名稱都由其本地名稱和命名空間名稱(即,它的通用名稱或擴展名稱)組合而成。

對於沒有命名空間的元素和屬性節點,執行 XPath 查詢是相當簡單的。下面的程序可用於從命令行查詢 XML 文檔,並將用來闡釋命名空間對 XPath 查詢的影響。

using System.Xml.XPath; 
using System.Xml; 
using System;
using System.IO; 

class XPathQuery{

public static string PrintError(Exception e, string errStr){

  if(e == null) 
    return errStr; 
  else
    return PrintError(e.InnerException, errStr + e.Message ); 
} 

 public static void Main(string[] args){

   if((args.Length == 0) || (args.Length % 2)!= 0){
     Console.WriteLine("Usage: xpathquery source query <zero or more 
prefix and namespace pairs>");
      return; 
   }
   
   try{
     
     //Load the file.
     XmlDocument doc = new XmlDocument(); 
     doc.Load(args[0]); 

     //create prefix<->namespace mappings (if any) 
     XmlNamespaceManager  nsMgr = new XmlNamespaceManager(doc.NameTable);

     for(int i=2; i < args.Length; i+= 2)
       nsMgr.AddNamespace(args[i], args[i + 1]); 

     //Query the document 
     XmlNodeList nodes = doc.SelectNodes(args[1], nsMgr); 

     //print output 
     foreach(XmlNode node in nodes)
       Console.WriteLine(node.OuterXml + "\n\n");

   }catch(XmlException xmle){
     Console.WriteLine("ERROR: XML Parse error occured because " + 
PrintError(xmle, null));
   }catch(FileNotFoundException fnfe){
     Console.WriteLine("ERROR: " + PrintError(fnfe, null));
   }catch(XPathException xpath){
     Console.WriteLine("ERROR: The following error occured while querying 
the document: " 
             + PrintError(xpath, null));
   }catch(Exception e){
     Console.WriteLine("UNEXPECTED ERROR" + PrintError(e, null));
   }
 }
}

假設下面的 XML 文檔不聲明任何命名空間,則查詢相當簡單,如以下代碼後面的示例所示。

<?xml version="1.0" encoding="utf-8" ?> 
<bookstore> 
  <book genre="autobiography">
    <title>The Autobiography of Benjamin Franklin</title>
    <author>
      <first-name>Benjamin</first-name>
      <last-name>Franklin</last-name>
    </author>
    <price>8.99</price>
  </book>
  <book genre="novel">
    <title>The Confidence Man</title>
    <author>
      <first-name>Herman</first-name>
      <last-name>Melville</last-name>
    </author>
    <price>11.99</price>
  </book>
</bookstore>

示例 1

  • xpathquery.exe bookstore.xml /bookstore/book/title 
    

    選擇所有作爲 book 元素(其父級爲 bookstore 元素)子級的標題元素,並返回如下結果:

       <title>The Autobiography of Benjamin Franklin</title>
       <title>The Confidence Man</title>
    
  • xpathquery.exe bookstore.xml //@genre 
    

    選擇文檔中的所有 genre 屬性並返回如下結果:

       genre="autobiography"
       genre="novel"
    
  • xpathquery.exe bookstore.xml //title[(../author/first-name = 'Herman')] 
    

    選擇作者名爲 "Herman" 的所有標題並返回如下結果:

       <title>The Confidence Man</title>  
    

    但是,在將命名空間添加到混合名稱中之後,事情不再那麼簡單。除了向某個 book 元素中添加了命名空間和一個屬性以外,下面的文件與原始文件相同。

    <bookstore xmlns="urn:xmlns:25hoursaday-com:bookstore">
      <book genre="autobiography">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
          <first-name>Benjamin</first-name>
          <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
      </book>
      <bk:book genre="novel" bk:genre="fiction" 
    xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">
        <bk:title>The Confidence Man</bk:title>
        <bk:author>
          <bk:first-name>Herman</bk:first-name>
          <bk:last-name>Melville</bk:last-name>
        </bk:author>
        <bk:price>11.99</bk:price>
      </bk:book>
    </bookstore>
    

    請注意,默認命名空間位於整個 XML 文檔的作用域內,而將前綴

    bk
    映射到命名空間名稱
    urn:xmlns:25hoursaday-com:bookstore
    的命名空間聲明只位於第二個 book 元素的作用域內。

示例 2

  • xpathquery.exe bookstore.xml /bookstore/book/title 
    

    選擇所有作爲 book 元素(其父級爲 bookstore 元素)子級的標題元素,這不返回任何結果。

  • xpathquery.exe bookstore.xml //@genre 
    

    選擇文檔中的所有

    genre
    屬性並返回如下結果:
       genre="autobiography"
       genre="novel"
    
  • xpathquery.exe bookstore.xml //title[(../author/first-name = 'Herman')] 
    

    選擇作者名字爲 "Herman" 的所有標題,這不返回任何結果。

    第一個查詢之所以不返回任何結果,是因爲 XPath 查詢中不帶前綴的名稱應用於沒有命名空間的元素或屬性。在沒有命名空間的目標文檔中沒有

    bookstore
    book
    title
    元素。第二個查詢返回沒有命名空間的所有屬性節點。儘管命名空間聲明位於由該查詢返回的這兩個屬性節點的作用域內,但是由於命名空間聲明不應用於沒有前綴名稱的屬性,所以它們沒有命名空間。出於與第一個查詢同樣的原因,第三個查詢不返回任何結果。

    可通過以下方法來執行能夠識別命名空間的 XPath 查詢:提供一個到 XPath 引擎的前綴-命名空間映射,然後在該查詢中使用這些前綴。所提供的前綴不必與目標文檔中的命名空間-前綴映射相同,而且它們必須是非空前綴。

示例 3

  • xpathquery.exe bookstore.xml /b:bookstore/b:book/b:title b urn:xmlns:25hoursaday-com:bookstore 
    

    選擇所有作爲 book 元素(其父級爲 bookstore 元素)子級的標題元素,並返回如下結果:

        <title xmlns="urn:xmlns:25hoursaday-com:bookstore">The Autobiography of Benjamin Franklin</title>
       <bk:title xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">The Confidence Man</bk:title>
    
  • xpathquery.exe bookstore.xml //@b:genre b urn:xmlns:25hoursaday-com:bookstore

    選擇文檔中來自 "urn:xmlns:25hoursaday-com:bookstore" 命名空間的所有

    genre
    屬性並返回如下結果:
       bk:genre="fiction"
    
  • xpathquery.exe bookstore.xml //bk:title[(../bk:author/bk:first-name = 'Herman')] bk urn:xmlns:25hoursaday-com:bookstore 
    

    選擇作者名爲 "Herman" 的所有標題並返回如下結果:

       <bk:title xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">The Confidence Man</bk:title>
    

    注意 示例 3 與示例 1 和 2 相同,但是它被重寫爲能夠識別命名空間。

有關使用 XPath 的詳細信息,請閱讀 Aaron Skonnard 的文章 Addressing Infosets with XPath 並查看 ZVON.org XPath tutorial 上的示例。

XSLT 和命名空間

W3C XSL 轉換 (XSLT) 建議描述一種基於 XML 的語言,用於將 XML 文檔轉換爲其他 XML 文檔。XSLT 轉換又稱爲 XML 樣式表,它使用模式 (XPath) 來與目標文檔的各個方面進行匹配。在與目標文檔中的節點進行匹配時,可以對那些指定成功匹配輸出結果的模板進行實例化,並使用這些模板來轉換文檔。

對命名空間的支持已緊密集成到 XSLT 中,特別是由於 XPath 用於與源文檔中的節點進行匹配。在 XSLT 內部的 XPath 表達式中使用命名空間比使用 DOM 方便得多。

隨後的示例包含:

  • 一個用於從命令行執行轉換的程序。

  • 一個 XSLT 樣式表,當它在來自

    urn:xmlns:25hoursaday-com:bookstore
    命名空間的
    bookstore
    文檔中運行時,打印源 XML 文檔中來自
    urn:xmlns:25hoursaday-com:bookstore
    的所有
    title
    元素。
  • 得到的輸出結果。

程序

Imports System.Xml.Xsl
Imports System.Xml
Imports System
Imports System.IO

Class Transformer

   Public Shared Function PrintError(e As Exception, errStr As String) As String
      
      If e Is Nothing Then
         Return errStr
      Else
         Return PrintError(e.InnerException, errStr + e.Message)
      End If 
   End Function 'PrintError
   
   'Entry point which delegates to C-style main Private Function
   Public Overloads Shared Sub Main()
      Run(System.Environment.GetCommandLineArgs())
   End Sub 'Main 
   
   
   Overloads Public Shared Sub Run(args() As String)
      
      If args.Length <> 2 Then
         Console.WriteLine("Usage: xslt source stylesheet")
         Return
      End If
      
      Try
         
         'Create the XslTransform object.
         Dim xslt As New XslTransform()
         
         'Load the stylesheet.
         xslt.Load(args(1))
         
         'Transform the file.
         Dim doc As New XmlDocument()
         doc.Load(args(0))
         
         xslt.Transform(doc, Nothing, Console.Out)
      
      Catch xmle As XmlException
         Console.WriteLine(("ERROR: XML Parse error occured because " + 
PrintError(xmle, Nothing)))
      Catch fnfe As FileNotFoundException
         Console.WriteLine(("ERROR: " + PrintError(fnfe, Nothing)))
      Catch xslte As XsltException
         Console.WriteLine(("ERROR: The following error occured while 
transforming the document: " + PrintError(xslte, Nothing)))
      Catch e As Exception
         Console.WriteLine(("UNEXPECTED ERROR" + PrintError(e, Nothing)))
      End Try
   End Sub
End Class 'Transformer

XSLT 樣式表

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
   xmlns:b="urn:xmlns:25hoursaday-com:bookstore">

<xsl:template match="b:bookstore">
<book-titles>
<xsl:apply-templates select="b:book/b:title"/>
</book-titles>
</xsl:template>

<xsl:template match="b:title"> 
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>

輸出

<?xml version="1.0" ?>
<book-titles xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
xmlns:ext="urn:my_extensions" xmlns:b="urn:xmlns:25hoursaday-com:bookstore">
<title xmlns="urn:xmlns:25hoursaday-com:bookstore">The Autobiography of 
Benjamin Franklin</title>
<bk:title xmlns="urn:xmlns:25hoursaday-com:bookstore" 
xmlns:bk="urn:xmlns:25hoursaday-com:bookstore">The Confidence 
Man</bk:title>
</book-titles>

請注意,該樣式表中的命名空間聲明結束於輸出 XML 文檔的根節點上。另請注意,XSLT 命名空間未包括在輸出 XML 文檔中。

從 XSLT 轉換的輸出結果生成 XSLT 樣式表有些麻煩,這是由於處理器必須能夠從實際的樣式表指令確定輸出元素。我發現可以通過兩種方法來解決此問題,我將通過顯示一些樣式表來闡釋這兩種方法,這些樣式表生成下面的 XMLT 樣式表作爲輸出結果。

<xslt:stylesheet version="1.0" 
 xmlns:xslt="http://www.w3.org/1999/XSL/Transform">
<xslt:output method="text"/>
<xslt:template match="/"><xslt:text>HELLO WORLD</xslt:text></xslt:template>
</xslt:stylesheet>

第一種方法涉及到創建一個包含要創建的樣式表的變量,然後結合使用

value-of
disable-output-escaping
屬性來創建該樣式表。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml"  encoding="utf-8"/> 
  <xsl:variable name="stylesheet">
 &lt;xslt:stylesheet version="1.0" 
 xmlns:xslt="http://www.w3.org/1999/XSL/Transform"&gt;
&lt;xslt:output method="text"/&gt;
&lt;xslt:template match="/"&gt;&lt;xslt:text&gt;HELLO 
WORLD&lt;/xslt:text&gt;&lt;/xslt:template&gt;
&lt;/xslt:stylesheet&gt;
 </xsl:variable> 
 <xsl:template match="/">
  <xsl:value-of select="$stylesheet" disable-output-escaping="yes" /> 

  </xsl:template>
  </xsl:stylesheet>

如果所創建的樣式表可以方便地進行分區,以便放在變量中,則第一種方法非常有效。儘管此方法快速方便,但是它還會歸入雜項 類別,這在面臨需要靈活性的任何情況時往往會變得難以處理。例如,如果在創建新樣式表時涉及到創建許多動態文本並且與樣式表指令交叉在一起,則相對於上面提到的雜項,以下方法會更適合。

<xslt:stylesheet version="1.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
 xmlns:alias="http://www.w3.org/1999/XSL/Transform-alias">
  <xslt:output method="xml"  encoding="utf-8"/> 

 <xslt:namespace-alias stylesheet-prefix="alias" result-prefix="xslt"/> 

 <xslt:template match="/">
 <alias:stylesheet version="1.0">
<alias:output method="text"/>
<alias:template match="/"><alias:text>HELLO 
WORLD</alias:text></alias:template>
</alias:stylesheet>
  </xslt:template>

  </xslt:stylesheet>

上面的文檔使用

namespace-alias
指令,將
alias
前綴及其所綁定到的命名空間名稱替換爲
xslt
前綴及其所綁定到的命名空間名稱。

命名空間還用於指定擴展 XSLT 的機制。可以創建執行方式與 XSLT 函數相同的、帶有命名空間前綴的函數。同樣,某些命名空間中的元素可被視爲對 XSLT 的擴展,並且可以像執行轉換指令(如

template
copy
value-of
等)那樣執行。下面是 Hello World 程序的示例,該程序使用基於命名空間的擴展函數來打印簽名問候語。
<stylesheet version="1.0" 
 xmlns="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
 xmlns:newfunc="urn:my-newfunc">
 <output method="text"/>

<template match="/">
 <value-of select="newfunc:SayHello()" />
</template>

 <msxsl:script language="JavaScript" implements-prefix="newfunc">
        function SayHello() {
           return "Hello World";
        }
 </msxsl:script>
</stylesheet>

XML 命名空間警告

與任何有用的工具一樣,XML 中的命名空間可能會被錯誤地使用,而且它還具有許多微妙之處,可能會產生問題(如果用戶沒有意識到這些微妙處)。本節重點介紹 XML 命名空間的用戶通常會在哪些方面遇到問題或面臨誤解。

版本控制和命名空間

在實際中,主要有兩種機制來創建不同版本的 XML 實例文檔。一種方法是像在 XSLT 中那樣使用根元素的 version 屬性,另一種方法是將元素的命名空間名稱用作版本控制機制。目前,基於命名空間的版本控制非常普遍,對於 W3C 尤其如此。W3C 已經將該機制用於各種 XML 技術,包括 SOAP、XHTML、XML 架構和 RDF。對於那些使用命名空間控制版本的文檔,其命名空間 URI 通常採用如下格式:

http://my.domain.example.org/product/[year/month][/area]

通過修改後續版本中的命名空間名稱來控制 XML 文檔的版本有一個主要問題,那就是這意味着處理這些文檔的、能夠識別 XML 命名空間的應用程序將不再處理這些文檔,並且將必須進行升級。這主要有益於版本不經常更改的文檔格式,但是如果在更改版本時會修改元素和屬性的語義,則會要求所有的處理器不再處理新版本,以防對它們產生錯誤理解。

另一方面,在許多情況下,讓 XML 文檔的版本控制機制基於根元素的 version 屬性就足夠了。version 屬性主要有益於文檔結構的更改可以向後兼容的情況。在以下情況下,使用 version 屬性都是非常明智的選擇:

  • 元素和屬性的語義將不會被修改。

  • 對文檔進行更改時涉及到添加元素和屬性,但是很少涉及到刪除它們。

  • 應用程序與各種版本的處理軟件之間的互操作性是必需的。

這兩種版本控制方法不是互斥的,它們可同時使用。例如,XSLT 既使用根元素的 version 屬性,又使用版本控制的命名空間 URI。version 屬性用於對 XML 文檔格式進行遞增式向後兼容的更改,而修改命名空間名稱是爲了對文檔的語義進行重大的更改。

文檔類型

正如在幾個有關各種 XML 相關郵件列表的哲學爭論中討論的那樣,術語文檔類型 容易引起誤解。在許多情況下,根元素的命名空間名稱可用於確定如何處理文檔,然而,這很難成爲一個一般性規則,並且如此聲明會違反 XML 命名空間的精神,因爲它們在設計上完全是爲了讓開發人員能夠混合和匹配 XML 詞彙表。

Rick Jelliffe 有關 XML-DEV 的帖子簡明扼要,它抓住了爲什麼認爲根命名空間的 URI 等同於文檔類型概念這一問題的本質。這個帖子的本質是,一個 XML 文檔可以有許多不同的類型,包括它的文檔類型(由它的文檔類型定義 (DTD) 指定)、它的 MIME 媒體類型、它的架構定義(由 xsi:schemaLocation 屬性指定)、它的文件擴展名以及它的根元素的命名空間名稱。因此,在許多情況下,根據用戶從哪個角度來檢查文檔,一個文檔將很有可能有許多不同的類型。

RDDL 文檔示例,請注意,它的根元素來自 XHTML 命名空間)和批註的映射架構(它們的根元素來自 W3C XML 架構命名空間)是 XML 文檔的兩個示例,在這些示例中,如果只是查看根元素的命名空間 URI,就會曲解實際文檔類型。

一言以蔽之,不能通過查看文檔根元素的命名空間 URI 來最終確定文檔的類型。一定要進行思考,否則是非常愚蠢的。

命名空間的未來

在 XML 領域中,有許多開發都側重於處理圍繞 XML 命名空間開發而產生的一些問題。首先,當前的 W3C XML 命名空間建議的草案沒有爲取消對已映射到前綴的命名空間的聲明提供機制。W3C XML 命名空間 v1.1 工作草案將提供一種機制來取消對實例文檔中前綴-命名空間映射的聲明,以糾正這種疏忽。

對於在試圖取消引用命名空間 URI 的內容時應當返回什麼內容存在着爭論,這導致在 XML 領域中引起富有爭議的辯論,並且還成爲 W3C 的 Technical Architecture Group 目前爭論的焦點。當前版本的 XML 命名空間建議不要求命名空間 URI 實際上是可解析的,因爲命名空間 URI 應當只是一個用作唯一標識符的命名空間名稱,而不是資源在 Internet 上的位置。

Tim Bray(XML 語言和 XML 命名空間建議最初的編輯之一)已經撰寫了一篇詳盡的論文,論述有關命名空間 URI 和命名空間文檔的問題,可以(或者也許不可以)從 URI 中檢索到這些文檔。此文包含在創建資源目錄描述語言 (RDDL) 時涉及到的許多推理。RDDL 設計用於創建命名空間文檔。

Dare Obasanjo 是 Microsoft 的 WebData 組的成員,該小組在 .NET 框架的 System.Xml 和 System.Data 命名空間、Microsoft XML 核心服務 (MSXML) 和 Microsoft 數據訪問組件 (MDAC) 中開發組件。

發佈了2 篇原創文章 · 獲贊 13 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章