Xsl模板應用基礎(二、程序轉換)


前文簡要介紹了Xsl語言與轉換流程,本文將演示使用C#實現Xml處理器,再回顧一下轉換流程:

 

從上圖可知,Xml處理器其實就是一個轉換方法,有輸入輸出,還有過程參數。下面是核心代碼片段:

 

 1     using System.Xml;
 2     using System.Xml.Xsl;
 3     ......
 4 
 5     /// <summary>
 6     /// 使用xsl格式化xml,結果寫入到Result參數。
 7     /// </summary>
 8     /// <param name="Xdom"></param>
 9     /// <param name="XslFile"></param>
10     /// <param name="Result">結果寫入到此流中</param>
11     public static void ConvertXsl(XDocument Xdom, string XslFile, Dictionary<string, object>Params, Stream Result)
12     {
13         XslCompiledTransform Process = GetXslProcess(XslFile);        
14         XsltArgumentList Arguments = InitArgusList(Params);
15         using (StreamWriter Writer = new StreamWriter(Result, Encoding.UTF8))
16         {
17             Process.Transform(Xdom.CreateReader(), Arguments, Writer);        
18         }
19     }
20 
21     /// <summary>
22     /// 初始化Xsl轉換過程需要的過程參數
23     /// </summary>
24     /// <param name="Argus"></param>
25     /// <returns></returns>
26     private static XsltArgumentList InitArgusList(Dictionary<string, object> Argus)
27     {
28         if (Argus == null) return null;
29         XsltArgumentList Arguments = new XsltArgumentList();
30         foreach (string key in Argus.Keys)
31         {            
32             if (key.StartsWith("urn:"))
33             {
34                 Arguments.AddExtensionObject(key, Argus[key]);
35             }
36             else
37             {                
38                 Arguments.AddParam(key, String.Empty, Argus[key] == null ? String.Empty : Argus[key]);
39             }
40         }
41         return Arguments;
42     }
43 
44     /// <summary>
45     /// 獲取指定Xsl模板編譯后的轉換器
46     /// </summary>
47     /// <param name="XslFile"></param>
48     /// <returns></returns>
49     private static XslCompiledTransform GetXslProcess(string XslFile)
50     {
51         string CacheKey = XslFile;
52         if (HttpRuntime.Cache[CacheKey] != null)
53         {
54             return (XslCompiledTransform)HttpRuntime.Cache[CacheKey];
55         }
56 
57         XmlReaderSettings Setting = new XmlReaderSettings();
58         Setting.ProhibitDtd = false;
59         Setting.XmlResolver = new XmlUrlResolver();
60         Setting.IgnoreComments = true;
61         Setting.IgnoreWhitespace = true;        
62 
63         XslCompiledTransform Xct = new XslCompiledTransform();
64         XsltSettings xs = new XsltSettings(true, true);        
65         XmlUrlResolver resolver = new XmlUrlResolver();
66         resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;
67         
68         //最后一次訪問緩存后,緩存對象繼續存活的時間,超過后自動被回收。
69         TimeSpan MaxAge = TimeSpan.FromMinutes(20);
70         using (FileStream Fs = new FileStream(XslFile, FileMode.Open, FileAccess.Read))
71         {            
72             using (XmlReader Xr = XmlReader.Create(Fs, Setting))
73             {
74                 Xct.Load(Xr, xs, resolver);
75                 System.Web.Caching.CacheDependency Dep = new System.Web.Caching.CacheDependency(XslFile);
76                 HttpRuntime.Cache.Insert(CacheKey, Xct, Dep, System.Web.Caching.Cache.NoAbsoluteExpiration, MaxAge);
77             }
78         }
79         return Xct;
80     }

上面提供一個公共方法 ConvertXsl() 供調用,第一個傳入的參數也可以改造為 XmlDocument 類型的實例,看個人喜好了。

第二個參數是XslFile文件的完整磁盤路徑。該參數在私有方法GetXslProcess()中使用,內部使用HttpRuntime.Cache容器緩存Xsl編譯后的實例對象,並依賴於Xsl文件的改動(一旦Xsl文件被修改,該緩存自動失效)。由於引入了緩存,使得Xsl的轉換變得非常快速高效。網上有些文章批評Xsl轉換低效,所舉例子大都沒有使用緩存機制,每次轉換都完整加載一次Xsl,效率自然低下。

第三個參數是轉換過程參數。為了簡化調用,參數類型設為Dictionary<string, object>。轉換參數有兩種,一種是值類型,內部通過AddParam(key, value)方法加入,value雖然是object類型,但一般是int、string、NodeSet簡單類型。另一種參數是通過AddExtensionObject(key, value)方法加入,key是Uri類型,value可以是類的實例,在Xsl文件中可以調用該實例的成員方法,這一點很酷,相當於在Xsl中調用C#的方法,幾乎就可以為所欲為了(查看MSDN中的XsltArgumentList )。上面封裝的方法中,對傳入第二種參數有一約定,即參數名為urn:開頭的參數,將被認作擴展對象加入。

 

下面是調用方法:

 1 public class Student
 2     {
 3         public string Name;
 4         public int Age;
 5         public string Say(string Words)
 6         {
 7             return "我是" + this.Name + ",今年" + this.Age.ToString() + "歲了。我想說:" + Words;
 8         }
 9     }
10     
11     public void Main () {
12         Dictionary<string, object> Params = new Dictionary<string, object>();
13         Student s = new Student();
14         s.Name = "摩羅叉";
15         s.Age = 13;
16         Params.Add("urn:ExtensionFunc", s);
17         Params.Add("time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
18         XDocument Xdom = XDocument.Load(Server.MapPath("student.xml"));
19         string XslFile = Server.MapPath("student.xsl");
20         ConvertXsl(Xdom, XslFile, Params, Response.OutputStream);
21     }

student.xml 與前文提供的一致,僅去除第二行的處理指令。student.xsl則更新如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!DOCTYPE xsl:stylesheet  [
 3     <!ENTITY nbsp   "&#160;">
 4     <!ENTITY copy   "&#169;">
 5     <!ENTITY reg    "&#174;">
 6     <!ENTITY trade  "&#8482;">
 7     <!ENTITY mdash  "&#8212;">
 8     <!ENTITY ldquo  "&#8220;">
 9     <!ENTITY rdquo  "&#8221;"> 
10     <!ENTITY pound  "&#163;">
11     <!ENTITY yen    "&#165;">
12     <!ENTITY euro   "&#8364;">
13 ]>
14 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:student="urn:ExtensionFunc" extension-element-prefixes="student">
15 <xsl:output method="html"/>
16 <xsl:param name="time" />
17 <xsl:template match="/">
18 <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html&gt;</xsl:text>
19 <html>
20     <head>
21         <title>學生清單</title>        
22     </head>
23     <body>
24         <table border="1" cellpadding="5" cellspacing="0">
25             <tr>
26                 <th>ID</th>
27                 <th>姓名</th>
28                 <th>年齡</th>
29                 <th>性別</th>
30             </tr>
31             <xsl:for-each select="/root/student">
32                 <tr>
33                     <td>
34                         <xsl:value-of select="@id"/>
35                     </td>
36                     <td>
37                         <xsl:value-of select="name"/>
38                     </td>
39                     <td>
40                         <xsl:value-of select="age"/>
41                     </td>
42                     <td>
43                         <xsl:choose>
44                             <xsl:when test="sex='male'">
45                                 <xsl:value-of select="'男'"/>
46                             </xsl:when>
47                             <xsl:when test="sex='female'">
48                                 <xsl:value-of select="'女'"/>
49                             </xsl:when>
50                             <xsl:otherwise>
51                                 <xsl:value-of select="'未知'"/>
52                             </xsl:otherwise>
53                         </xsl:choose>
54                     </td>
55                 </tr>
56             </xsl:for-each>
57         </table>
58         現在時間:<xsl:value-of select="$time"/><br/>
59         說點什么:<xsl:value-of select="student:Say('尼瑪!')"/><br/>
60         學生總數:<xsl:value-of select="student:GetTotal(/root/student)"/><br/>
61         書名:<xsl:value-of select="student:GetBooks()/root/book/@name"/><br/>
62     </body>
63 </html>
64 
65 </xsl:template>
66 </xsl:stylesheet>

 

從C#中的Main()方法可知,轉換過程,傳入兩個參數,第一個是Student類實例,參數名為urn:ExtensionFuncurn:后面的字符串表示傳入對象的命名空間,可以隨意定義(如果有多個對象,需保證唯一),另一個參數是當前服務器時間,參數名為time。這兩個參數在student.xsl文件中有相應的體現,xsl第16行<xsl:param name="time" />表示接收傳入值參數,參數名為time,調用時需使用 $time 才能得到參數值。另一個參數由於是類實例,需要在第14行先聲明接收的命名空間,定義前綴(簡稱),Xsl中聲明的命名空間必須與C#中定義的命名空間一致,然后在第59行使用簡稱調用 student:Say('xxx'),Say方法在 Student 類中定義。這就實現了Xsl的跨界引用,但凡是跨界,都能帶來無限的想象空間。

如果在Xsl中調用擴展方法,並且傳入當前上下文的節點,如(假如傳入的student實例實現了GetTotal方法):

1 <xsl:value-of select="student:GetTotal(/root/student)"/>

那么該方法將得到類型為 System.Xml.XPath.XPathNodeIterator 的參數:

1 public class Student
2     {
3         ......
4         public string GetTotal(XPathNodeIterator Iterator)
5         {
6             return Iterator.Count.ToString();                    
7         }
8     }

擴展方法還能返回XML節點與節點集合,供Xsl使用:

1 public XPathNavigator GetBooks()
2         {
3             XPathNavigator Nav = XDocument.Parse("<root><book name='金剛經'/></root>").CreateNavigator();
4             Nav.MoveToRoot();
5             return Nav;            
6         }

Xsl調用GetBooks():

1 <xsl:value-of select="student:GetBooks()/root/book/@name"/>

用瀏覽器訪問,得到最終結果:

 

 

本文使用C#實現了Xml+Xsl=>Html 的轉換過程(下載例子),並且舉例轉換過程中傳入的兩種參數的應用。在實際應用過程中,由於頁面的構造完全使用Xsl格式化,不再需要使用 System.Web.UI.Page 類,因此推薦在一般處理程序(HttpHandle)中執行Xsl轉換,這樣可以得到完整的頁面HTML代碼,或可再做一次服務端緩存,或者做基於HTTP協議的客戶端緩存,甚至手工實現Gzip傳輸,自由度比普通頁面程序(aspx)大一些。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM