SLT1.0 是W3C標准,主要用於對XML文檔的轉換,包括將XML轉換成HMTL,TEXT或者另外格式的XML文件.XSLT1.0可以與XPATH1.0標准一起使用,XPATH會告訴你要轉換的節點而XSLT則提供了一種機制來說明如何實現這種轉換。為了將源文檔轉換成想要的格式, 一個XSLT樣式文件往往包含一系列的規則,而要解釋這些規則, 需要依賴XSLT處理器,這個處理器實際上就是對XSLT1.0標准的實現,所以大家可以按照這個標准提供自己的實現,處理器可以根據標准元寶的所有特性來來執行規則,最終完成轉換的工作。
XSLT的處理機制
XSLT樣式表用於定義轉換邏輯,這些邏輯會應用於源XML文檔的樹狀形式的節點集上,然后生成樹狀結構的節點集做為輸出結果。
下面的XML文檔比較簡單,我們就以此做為源文件:
<!-- Emp.xml --> |
每個XML文檔必須有一個代表該文檔的根結點, 並且這個根結點的子節點可以包含文檔節點(具有唯一性,在上面的例子中就是<ROWSET>,大家要清楚根節點跟文檔節點的區別),注釋節點和指令結點。文檔節點可以包含任意數量的文本節點和元素節點,同時,這些子節點仍可以包含其他任意數量的子節點,這些節點以相互嵌套的方式形成了一棵樹。
所以,要實現文檔轉換, 必須要有兩個組成部分:
1 源XML文檔, 在內存中,它是以樹狀的節點集形式表現,了解DOM的人應該容易理解。
2 XSLT文檔,其中包含一系列的轉換規則。
XSLT本身也是XML格式的文檔,不同的是XSLT文檔支持相應的指令標簽,以實現轉換的功能, XSLT的文檔節點是<xsl:stylesheet>,該節點下面包含所有的轉換規則,每個規則一般都與XPATH關聯,XPATH表明,這個規則適用於哪個節點,當XSLT處理器在解釋源文檔的某個節點的時候,會查找匹配這個節點的規則。當然,這種規則也被稱做Template, 在XSLT中是用標簽<xsl:template>表示,該標簽有個match屬性來關聯XPATH表達式。比如,像下面的這個規則,它應用於源文檔的根節點,”/” 是XPATH表達式,說明這個規則適用於根節點。
<xsl:template match="/"> |
類似,像下面的模板:
<xsl:template match="ROWSET/ROW[ENAME]"> <!-- 輸出的內容:文本,屬性 等等. --> |
就僅作用於源文檔中的<ROW>節點集,一個<ROW>節點下面還含有<ENAME>子節點,並且這個<ROW>必須是<ROWSET>節點的直接子節點,才能應用這個規則。
為什么要用TEMPATE來表示一個規則呢?因為在應用這個規則的時候,包含在規則里面的文本和元素就像是個模板,每次XSLT處理程序在調用同個規則的時候,輸出的結果都是以這個模板為基礎的,模板保證了輸出的結構是一致。
讓我們看下,以下的規則會輸出什么:
<xsl:template match="ROWSET/ROW[ENAME]"> |
XSLT處理程序在解釋<ROW> 節點時,會查找匹配的規則,這個模板的match屬性值是“ROWSET/ROW[ENAME]”,剛好符合要求, 最后程序會調用這個模板,輸出結果。
當匹配的模板被實例化后,接下來還會做三件事情:
1) 模板中的文本和屬性直接輸出。任何不是以XSL命名空間開頭的都被認為是文本,像上面例子里的<Employee> 和 id 屬性,它們會以文本形式直接輸出。
2) 以大括號表示的元素,像{XPathExpr}, XSLT會計算它的值並將值以文本形式返回到當前的位置,我們可以理解為是一個表達式點位符。
3) XSLT命名空間下的任何元素,都以文檔中的先后順序被執行。比如執行<xsl:value-of>元素,處理程序會根據select屬性中的XPATH表達式獲取到值,並以文本節點的形式替換<xsl:value-of>元素。
基本的邏輯可以歸納為,當源文檔中的節點與XSLT中的某個規則匹配的時候,規則中的內容就會輸出到結果樹中。一旦你掌握了這個過程, 那整個XSLT處理模型就容易理解了。 給你一個源樹(XML文檔的樹狀表示)和XSLT樣式表,XSLT處理程序
依照樣式表中的規則說明的那樣,一步步的執行這些規則,最終將源文檔轉化成結果樹。
在執行源樹節點集的過程中, 會產生結果樹的一部分或稱做“片段”,當然,到最后這些片段會被整合在一起。當前正在被執行的節點,稱做當前節點, XSLT處理器會選擇所有適用於當前節點的模板,如果適用的模板有多個,處理器會根據內置的規則(這個后面再細說),從中選取一個最匹配的,因為針對一個節點只能使用一個模板。
執行的時候,XSLT處理器從根節點開始,它搜索匹配根節點的模板,一般來說, 匹配根節點的模板,它的match屬性等於"/",找到,處理器實例化該模板,並將內容輸出到結果樹, 通常都要執行這三個步驟來完成工作。
如果模板還包含需要處理其他節點的XSLT指令, 那么處理器會重復上面的步驟,搜索模板,應用模板,輸出結果,這是個循環的過程,直到所有的XSLT指令都被執行完成。執行完成后,轉化過程產生的結果樹,就是我們想要的目標文檔。
單模板實現
理論上說,只要定義一個模板就可以實現轉化的過程,接下來,我們會創建這樣的一個模板來進行說明,當然,在一個XSLT樣式表中定義多個模板,會給我們帶來更多好處,這個在后面詳細介紹。
像下面的樣式表,主要作用是將XML文檔轉換成HTML格式的文本。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
或者另外一種更簡潔的表示:
<!—這種方式,隱藏了匹配根節點的模板 --> <!-- |
當看到XSL命名空間的申明語句:xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ,你很自然的就會想到,當執行這個樣式表的時候,XSLT處理器會訪問這個URL地址。然而,這個申明的作用僅僅是做為唯一的字符串來標記XSLT的命名空間, 命名空間的作用是為了區別各自的標簽,因為XML是允許自定義標簽的,這會導致出現相同的標簽名的可能性大大增加,為了避免沖突,突顯自定義標簽的唯一性,引入了命名空間的概念。XSLT用XSL作用命名空間的別名, <xsl:template>, <xsl:for-each>, <xsl:value-of>這些標簽就表明他們是XSLT相關的標簽,XSLT處理器會根據XSL前輟來識別它們,如果你移除了XSL前輟,XSLT處理器就不可能識別出它們。
考慮一下以下的樣式表,它只包含一個模板,這個例子的目的就是將EMP.XML轉化成HTML。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
輸出結果:
![]() |
上面的模板混合了HTML的標簽<html>, <body>, <table>, <tr>, and <td>,和 <xsl:for-each> and <xsl:value-of>,當XSLT處理器實例化這個模板的時候,根節點就是當前節點,
<xsl:for-each>標簽 :
1) 選擇源XML樹中所有的"ROWSET"節點集。
2) 將選中的節點集做為當前正在處理的節點集。
3) 開始執行這些節點集。
針對節點集中的每個節點,<xsl:for-each>里的內容都被實例化到結果樹中,如果這個實例化后的片段,含有其他的XSLT元素,需要解釋執行,碰到<xsl:value-of>元素的話,會用XPATH表達式計算后的結果替換當前位置。
生成的HTML:
<html> |
在這個例子中, XSLT處理器僅僅執行與根節點匹配的模板, 所有的子節點處理都依靠執行<xsl:for-each>來完成。
如果模式表只用一個模板,那么我們可以用更簡潔的方式來實現,像<xsl:stylesheet> 和<xsl:template match="/">都可以不需要。這種情況下, 文字元素在模板中是做為第一個元素。但是你必須包含XSLT的命名空間,並且要加上xsl:version="1.0"屬性。
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
與前一個相比,寫法稍有不同,但它們輸出的結果是完全一樣的,而且后一種看起來更簡潔明了。
理解輸入和輸出選項
在之前的XSLT轉化過程,我們都有談到節點樹這個概念,節點的樹狀結構其實並不存在,它只是為了便於我們理解的一種邏輯形式,從XSLT處理器的角度來說,它就是一堆連續的符號,是其在內部執行源XML文檔和輸出轉換結果的過程中的邏輯表現形式,實際情況是:
1) 源文檔是以可讀的文本形式存在。
2) 轉化的結果需要以其他可讀的方式輸出,比如,以文本形式保存到文件中或以流的方式輸出到瀏覽器。
轉換的輸入信息必須是節點樹,這可以通過解析源XML文檔或者手動編程的方式構造出一個樹(通過DOM 或SAX提供的API)。
所有的XSLT轉換都是通過解析源節點樹,生成結果節點樹,當你的應用中有多個順序執行的轉化過程,那么一個轉化過程產生的節點樹會做了下個轉化過程的輸入,直到所有的轉化過程都結束為止,所有的節點集做為一個整體以字符流的方式輸出。這個過程稱做序列化結果樹。
根據XSLT 1.0規范的描述, 利用默認的序列化規則,在通用情況下可以使我們的XSLT文件看起來更簡潔。XSLT1.0用UTF-8做為轉化輸出結果默認的編碼集 同時還支持以下幾種輸出格式:
- 支持縮進的,格式規范的HTML , 它的類型是text/html
- 無縮進的XML, 沒有DOCTYPE屬性,它的類型是text/xml
拋開這些默認的選項, 標准的XSLT語法需要以<xsl:stylesheet>開頭,然后包含<xsl:output>子元素,通過這個子元素來控制輸出序列化過程。
要想明白如何控制序列化,最主要的就是理解output元素中的method屬性,這個屬性決定XSLT處理器如何將結果集序列化到輸出流。XSLT 1.0支持三種不同的輸出選項:
- <xsl:output method="xml"/>, 這個是默認項,輸出XML格式。
- <xsl:output method="html"/>, 如果結果樹的文檔結點的<html>,這個就是默認項。要注意的是,它的輸出並不是格式良好的XML。
- <xsl:output method="text"/>, 這種方式只會將結果樹中的文本結點順序輸出。一般應用於,將XML轉化成編程相關的源代碼,郵件內容或其它的文本輸出。
考慮下面文檔中的例子:
<!-- King.xml --> |
用下面的XSLT樣式表來轉化King.xml,將 <ROWSET>節點轉化成 <Invitation>:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
輸出結果:
<?xml version="1.0"?> |
記住,XSLT樣式表是格式良好的文檔,所以有些特殊字符需要轉義,像“&”轉義成“&”和“<”轉成“<”
還要注意的就是像“&”這樣的數值型實體字符,它是數字“38”的Unicode編碼形式。如果你習慣這種十六進制的表示,像換行,你可以用
來代替。
指定output為html, 下面的樣式表會將<ROWSET>轉化成簡單的,包含圖片的HTML頁面。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
用這個樣式表來轉化King.xml,輸出的結果:
<html> |
如果指定output為text將<ROWSET>轉化成文本,那么輸出的結果不包含任何標簽:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
結果:
Hello King & Family, |
注意, 我們轉化的樣式表中用<xsl:text>來處理文本內容,一般來說, 空白字符在樣式表中是被忽略的, 所以可以實現標簽的縮進,以達到更好的可讀性。 但是,文本內容中的空格需要被保留,<xsl:text>可以幫助我們實現這個目的,這樣,空白符就會出現在輸出的文本中。除了空白字符外,還有換行符和TAB(制表符),利用<xsl:text>元素,這些符號都會被逐字保留。上面例子中的“
”代表回車換行符。
下圖說明了源文檔,源節點樹,結果節點樹和利用這三部分實現的序列化以及通過指定output,輸出不同的格式。
除了outpu屬性, 還有其它幾個屬性可以用來控件輸出行為。
![]() |
多模板實現靈活性
我們都清楚, 樣式表包含一組規則, 當我們用單個模板方式的時候,整個樣式表就只有一個規則:“匹配源文件的根節點,執行其中所有的XSLT指令。”這種方式,就像我們在Java編碼的時候,將所有的邏都放在一個main()函數里,肯定會有人贊成,有人反對。
public class doit { |
做為開發人員,剛入門的時候會覺得將所有實現都放在單個方法里比較容易理解,但他們很快就會發現,當邏輯越來越復雜的時候,在單個方法可能有很多可以共用的部分,如果能將它們單獨做為一個方法, 可以更好的提供代碼的可重用性,多模板也是基本這個考慮。如果采用多模板,我們也可以利用人家已經實現的規則,就好比站在巨人的肩膀上,可以讓你節約時間,而且你也可以新建一個自己規則,替換老的。
我們可以發現,XSLT編程與JAVA編程在很多方面的類似性。在JAVA里,每個方法是包含形為的整體而且可以被重寫。如果你實現一個類,並將所有的代碼邏輯都放在main()函數里,那么要是有人准備擴展你的代碼,那就只能重寫main()方法,盡管有時候,他們只是需要一個很小的改動。那最有效的方式,就是將一個方法中的邏輯拆分成幾個子方法,而且這些子方法應用易於重用,易於重寫。
在XSLT中, 模板是形為和重寫的基本單元。 就像上面提到的JAVA一樣,如果你將樣式表中的邏輯分成多個可重用的模板, 那么其他人就有可能繼承你的模板,然后調用你寫好的模板或者重寫模板以實現他們自己的行為邏輯。
根據每個轉化任務來拆分模板是最有效果的。你的模板越容易被調用,越容易被人重寫,就說明你的拆分越合理。
應用多模板
下面的例子中, 我們會將上面提到的單個模板進行細化,分成多個模板,細化后的每個模板都對應源文檔中的一個元素,負責對應元素的轉化工作。每個模板都用<xsl:apply-templates>指令告訴XSLT處理器,如果當前元素還有子元素,需要遞歸遍歷,直到所有的節點都處理完成。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> |
我們可以舉其中的某個模板為例:
<xsl:template match="ROWSET"> |
它的含義是: 對於源文檔中的<ROWSET>元素, XSLT處理器會應用這個模板來進行轉化,該模板會在結果樹中構建一個<table>元素,因為模板包含<xsl:apply-templates/>指令,處理器會繼續查找<ROWSET>元素的所有子節點,然后用相應節點的模板來轉化,直到所有的節點都轉化完成,最終,各個節點的轉化片段組成大的結果樹,做為<table>元素的子元素。
通常情況,處理器會從源文檔的根節點開始,搜索匹配的模板,在我們的樣式表中就是match="/"的那個模板,“/”符號就是表示根節點。所有這個根節點就做為當前節點被實例化。根節點模板構造出<html> 和<body> ,然后調用<xsl:apply-templates>去處理根節點的所有子節點。 這些子節點包含一個注釋節點和一個元素節點<ROWSET>,為了構造這些節點的結果樹片段,處理器按順序執行所有的子節點。注釋節點會被忽略(這個稍后解釋),對於<ROWSET>節點,會有相匹配的模板(match等於"ROWSET")來處理。
下面的圖說明XSLT處理器的順序處理的過程。
![]() |
所有的模板都會被實例化,然后輸出到結果樹中。根節點模板會輸出<html> 和<body>元素,"ROWSET"模板輸出<table>,嵌套在<body>元素里面,接下來,執行<xsl:apply-templates>指令,匹配所有的<ROWSET>子節點,<ROWSET>節點包含以下四個節點,按順序排列:
1. 空白節點
2. <ROW> 節點
3. 空白節點
4. <ROW> 節點
針對這些節點,處理器會查找匹配的模板,實例化模板,然后通過模板轉化節點,輸出到結果樹中。對於空白節點,默認情況下,系統會直接拷貝空白字符,而match等於"ROW"的模板會構造出兩個<tr>元素,然后繼續處理其他節點。
轉化完成的結果跟單個模板輸出的結果是完全一致的,但是,在接下來的幾個例子中, 多模板方式會顯現出其強大的好處。
理解內置模板
在繼續之前, 我們需要解釋一下,為什么注釋節點會被處理器忽略? 對於”7839“, ”KING“, “7788, 和“SCOTT”這樣的空白節點和文本節點處理器是如何進行轉化的?
要解答這些問題,不得不提到XSLT中的內置模板,這些內置模板是XSLT處理器的默認組成部分。
<xsl:template match="/|*"> 匹配根節點或任何節點,這個內置模板不輸出任何東西,但會告訴處理器去執行源文檔當前節點下的所有子節點。 匹配文本節點或屬性節點,將當前節點的值輸出。 匹配指令節點或注釋節點,但什么也不做。 |
為什么需要內置模板,設想一下,如果有人只想匹配源文檔中的某個節點,但是默認情況下,XSLT處理器都是從根節點開始匹配,如果沒有內置模板,系統會提示模板不存在就會報錯,要是每個開發人員都要將這些模板在自己的樣式表中都實現,會使樣式表看起來不夠簡潔。
會了更好的理解內置模板,我們會用下面的樣式表來轉化文檔”Emp.xml “,該樣式表中不包含任何模板:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> |
得到的結果是:
<?xml version = '1.0' encoding = 'UTF-8'?>
|
處理器用內置的模板來匹配源文檔中的元素,對於根節點元素,內置模板不會做任何的輸出,只會循環遍歷它的子元素,當子元素是空白元素的時候或者”7839“, ”KING“, “7788, 和“SCOTT”這樣的文本元素,就會用內置的text()來匹配,調用<xsl:value-of select="."/>來拷貝當前文本節點的元素值到結果樹中。 相應的, 結果樹就是源文檔中的一堆文本內容,來自任何層次的節點,以文檔中顯示的順序輸出。盡管這很有意思,但我們不會將這種不包含任何模板的樣式表用在實際的項目中。
通配符匹配和空白字符的處理
讓我們看下下面列出的幾個模板:
<xsl:template match="EMPNO"> |
實際上,這兩個模板做同樣的事情,它們都用來匹配<ROW>下面的兩個不同的子節點, 然后構建一個表格元素<td>,其中包含相應子節點的轉化結果樹。但是,我們要是在<ROW>增加新的節點,叫做<COMPANY>,<DEPTNO>,那是不是我們還要建立兩個新的,類似的模板呢,XSLT為我們提供了更好的解決方案,通配符。在XPATH表達式中,我們可以用通配符來指定某個結點下面的所有子節點,像這樣”ROW/*“。用這種方式,可以不再需要為每個子節點設置一個匹配模板,而只要用一個泛型模板就足夠了。
<!-- Match any element child of a ROW --> |
用通用的方式實現的模板,例子如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
輸出的結果與之前的一致,但是樣式表看起來更簡潔。
等等, 好像跟之前的輸出比較起來,還是有不一樣的地方。
這段文本並沒有像預期的那樣縮進,但是在單模板樣式表中,輸出的結果是排版良好的,所有節點都有縮進。
明白導致這個問題的原因很重要,因為這關系到XSLT如何處理源文檔中的空白符,回想一下,Emp.xml文檔的縮進是通過空白字符和回車符實現的。如果我們將這些都顯現出來的話,應該是下面的樣子。
當執行匹配<ROWSET>元素的模板的時候,XSLT處理器會構建一個<table>標簽,接着循環處理<ROWSET>的所有子節點,<ROWSET>包含下面幾個節點:
1. 文本節點,包含空白字符用來縮進:carriage return, space, space
2. <ROW> 節點
3. 文本節點,包含空白字符用來縮進:carriage return, space, space
4. <ROW> 節點
用多模板方式,XSLT處理器順序執行<ROWSET>的所有子節點,查找匹配的模板。當匹配第一個空白節點的時候,因為沒有明確的模板,處理器會調用內置模板"text()|@*"來處理這個節點,這個模板會將空白字符直接拷貝到結果樹,對於模板來說,空白節點跟文本節點是一樣的,同時,回車符也被直接輸出到結果樹中,就這是導致縮進不一致的問題。
那么單模板方式為什么沒有這個問題? 單模板在匹配根節點后, 通過執行<xsl:for-each>指令來選擇節點集,這些節點集中不包含空白節點,所以不存在上面提到的困擾。
要解決這個問題, 我們需要告訴XSLT處理器在轉化的時候,剔除這些節點,要實現這個功能, 要用到<xsl:strip-space>指令,這個指令必須放在樣式表的頂部。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
與之相反,如果你要保留某個元素的空白節點,需要用<xsl:preservespace>,它同樣包含有elements屬性。默認情況下,XSLT處理器保留所有元素的空白節點。
不同模式下的節點處理
前面例子中,輸出的結果只有數據信息,並沒有包含表頭,
為了創建一個通用的方式來生成表頭,我們必須遍歷所有的<ROW>元素,然后取出它子節點的名稱做為表格的頭單元。然而,我們已經有了一個匹配“ROW/*”的模板來處理<ROW>元素的子節點,現在為了創建表頭,也需要處理這些節點,如果不同的模板有相同的MATCH屬性,XSLT處理會根據優先規則只采用其中的一個,那么如何來區分這兩種不同的應用呢,XSLT為模板提供了另外一個屬性MODE:
- 假設我們指定了MODE屬性值為"ColumnHeaders",那么它就與原來沒有MODE屬性的模板區分開來,它們雖然是處理相同的節點,但邏輯上完全不同。
<xsl:template match="ROW/*" mode="ColumnHeaders"> |
- 在用<xsl:apply-templates />調用模板時,必須指定MODE屬性,像<xsl:apply-templates mode="ColumnHeaders"/>
<xsl:template match="ROWSET"> |
但是這樣的寫法有問題,生成表頭的模板會匹配<ROWSET>下面的所有<ROW> 節點,根據<ROW> 節點的個數,會生成重復的表頭信息,實際上, 我們只要處理一個<ROW> 節點就夠了。
解決這個問題非常簡單,只要保證我們的XPATH表達式只選擇一個<ROW> 節點就行,修改<xsl:apply-templates mode="ColumnHeaders"/>為<xsl:apply-templates select="ROW[1]/*" mode="ColumnHeaders"/>。
重用和定制已有的模板
我們曾經提到過,利用多模板方式,可以重復利用已有的模板規則,甚至可以用新的模板替換老的模板,比如我們要生成一張類似上面的表格,不同的是,對於工資大於2000的行,要對其進行高亮顯示,那我們要如何實現呢?
假設上面提到過的一些模板都已經存放在了樣式表文件TableBaseWithCSS.xsl中,然后我們重新建了新的樣式表EmpOver2000.xsl,這個文件包含新的模板,並且用<xsl:import>指定將TableBaseWithCSS.xsl引入到新的樣式表中,大家都知道,TableBaseWithCSS.xsl中已經定義了轉化表頭和行的基本模板。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
當用EmpOver2000.xsl這個樣式表來轉化源文檔的時候,XSLT處理器會查找<ROWSET>節點下的所有<ROW>,之前,就只有一個模板匹配這個<ROW>節點,但在新的樣式表中,我們創建了match值為ROW[SAL>2000]"的模板,這意味着對於當前節點集中的<ROW>節點,如果<SAL>這個值大於2000,處理器就會發現有兩個模板匹配這個節點,我們說過,處理器只會選擇一個最合適的模板來進行匹配,在這里ROW[SAL>2000]的范圍更具體,所以更適合。
讓我們再舉幾個例子:
- 格式化奇數行
- 格式化偶數行
- 將DEPTNO 等於20的行,輸出“Classified”
下面是樣式表,包含要用到的所有模板:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 匹配所有的奇數行--> |
position()函數用於取得當前的結點位置,mod 是取余操作符。
經過實驗,ROW[DEPTNO=20]模板從來沒用被調用過,這就說明,如果模板的優先級相同的話,處理器會永遠選擇最新的模板,比如當前樣式表中的模板優於被引用樣式表中的,文件中位置靠后的模板優先前面的。
使用Priorities來解決模板沖突
XSLT處理器在選擇合適的模板時,遵守下面的原則:
- 通配符“*”低於指定某個節點,像ROWSET
- 某個節點低於其中帶有查詢條件的節點,像ROWSET[predicate]或者ROWSET/ROW
根據這種具體程序來區別多模板,萬一,有多個模板,它們的程度都是一樣的,處理器又該如何選擇呢?一種方式就是利用priority屬性,priority="realnumber"可以是任意的正負值,當處理器無法根據規則選出最恰當的模板時,模板擁有較高priority就會被選中,priority大於0.5會使你自定義的模板比內置的要優先使用。
所以,當我們將priority="2"加到模板ROW[DEPTNO=20]中,比起匹配奇,偶行的模板,這個模板就有更高的優先級,在處理DEPTNO等於20的那行時,模板ROW[DEPTNO=20]會優先被處理器使用。
定義命名模板
接下來,我們看個格式化數字的例子,下面的樣式表有個format-number()函數,它的作用就是將數值轉換成指定的格式。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
這里,我們還是要引用TableBaseWithCSS.xsl,因為要用到它里面的模板,當前的樣式表重寫了匹配節點”ROW/SAL“的模板,而且用了另外的方式來處理變化的行,原來的方式是定義兩個模板來處理奇行和偶行,現在只需要一個模板就完成這個功能:
<tr class="tr{position() mod 2}"><xsl:apply-templates/></tr> |
這個模板會構造<tr>元素到結果樹,同時里面包含一個class屬性,根據當前行是奇數或偶數,它的值在tr0和 tr1之間變化。CSS文件的定義如下:
body { font-family: Verdana; font-size: 8pt } |
如果,你需要經常對數值進行格式化,像是對貨幣格式的轉換,最好是再建一個模板,以方便重用。我們可以用name屬性替換<xsl:template>的match屬性。
<xsl:template name="moneyCell"> |
然后,無論什么時候我們想調用模板,只要執行帶name屬性的<xsl:calltemplate>指令。
<xsl:call-template name="moneyCell"/> |
命名模板從來不會自動被XSLT處理器執行,除非在樣式表中明確的調用。當用<xsl:call-template>調用命名模板的時候,命名模板里面的字面元素和XSL指令會被實例化,就像它們就位於調用它的模板的當前位置。
與<xsl:apply-templates select="pattern"/>不同的是,<xsl:call-template>不會改變當前正在處理的結點,被調用的模板跟調用它的使用相同的節點,而xsl:apply-templates 會根據select屬性的值改變節點位置,理解這一點非常重要。
像其他模板一樣,命名模板可以被放在其他的文件里,相當於一個“方法庫”文件,從而被其它樣式表所引用。
常見錯誤當你在樣式表中混搭使用基於match的模板跟命名模板的時候,會經常不經意出現錯誤:
|