设为首页收藏本站language 语言切换
查看: 1532|回复: 0
收起左侧

在.Net中扩展Xslt功能之脚本篇

[复制链接]
发表于 2010-2-25 10:28:36 | 显示全部楼层 |阅读模式
<p ><center><font color="#000099"><strong>为什么要扩展Xslt样式表功能</strong></font></center><p >基于Xml的可扩展样式表语言Xsl及其转换Xslt实现了将Xml源文件从一种格式到另一种格式的转换。通过在xsl文件中定义一系列遵守规则的模板,匹配这些模板的Xml文档部分就能够被转换为可显示的Html页面文件、符合其他需要的Xml文件或除此之外其他结构的文件。当在Xslt文档中这样做时,没有明显的流程控制,也没有传统编程意义上的算法,甚至不需要任何程序代码和编程语言。绝大多数时候,这能够很好地完成任务。<p >任何一门语言都不可能绝对完善,而Xsl毕竟只是一门标记语言,单纯依靠它并不能完成所有任务。如同没有XPath、Xslt时,Xml充其量只能是一些标记的集合,什么也不能做一样。尤其,如果我们所需求的不仅仅是改变文档的显示方式或结构,我们还需要对文档进行更多的控制时,单纯使用Xslt的局限性就暴露出来了。比如,获取用户键入的参数以控制转换输出、连接外部数据源以及引用某种计算的结果等等,这些情况下,单纯使用Xslt这样的描述语言无法提供满意的解决方案。而我们知道,程序代码会很轻易地解决这些问题。<p ><center><font color="#000099"><strong>使用嵌入式脚本扩展Xslt样式表功能</strong></font></center><p >Xml是易于扩展的,这也正是其短时间内能被广泛接受的原因之一。而Xslt完全基于xml实现,它应该也是易于扩展的。事实的确如此,W3C推荐标准的Xslt提供了针对不同处理器/解析器的专门的扩展。基本上,不同处理器/解析器可以在Xpath表达式、Xslt模板主体内容或顶级元素中使用扩展函数。而且,同任何程序语言的函数/方法一样,这些扩展函数可以用来完成所要求的子功能,它们也能够“一次定义,多次调用”。更重要地,这些嵌入脚本函数不仅能在xslt标记语言中调用,还能够运用在高级语言中。脚本语言所提供的组功能比纯Xslt所提供的功能更丰富,经常可以用来对文档内容进行更复杂的操作(例如对其应用科学计算或访问外部信息源等),这个特性大大扩展了xslt的功能。<p >实现时,需要在一个指定的命名空间中限定所创建的扩展函数,并且在特定的Xslt处理器中提供实现。之所以必须使用命名空间,是因为命名空间不仅可以防止不同来源的Xslt名称间的冲突。更重要的是它还将扩展予以标识,这样其他的处理器就可以忽略这个专门针对特定处理器的实现,并依靠这个命名空间在Xslt其他部分引用扩展函数。<p >MSXML XSLT处理器使用<ccid_code>&lt;msxsl:script&gt;</ccid_code>元素及其属性implements-prefix实现并扩展函数以提供脚本级支持。微软特别指定的命名空间允许MSXML XSLT处理器在样式表内定位脚本代码。<ccid_code>&lt;msxsl:script&gt;</ccid_code>元素的language属性告诉运行期脚本使用哪个解释程序,而implements-prefix属性值是扩展函数的前缀,这个前缀xslt文档的其他地方被声明。定义这些属性之后,Xslt处理器就能够使用这些信息调用解释器运行时脚本(如C#编译器)并在<ccid_code>&lt;msxsl:script&gt;</ccid_code>元素中实现扩展函数的代码编写。在样式表的其他地方,依靠<ccid_code>&lt;xsl:value-of&gt;</ccid_code>元素提供输出的模板通过seletct属性与扩展函数相关联,比如,执行并输出扩展函数的计算结果。<p ><ccid_code>&lt;msxsl:script&gt;</ccid_code>元素定义如下:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;msxsl:script language=&quot;language-name&quot;  implements-prefix=&quot;prefix of user's namespace&quot;&gt;&lt;/msxsl:script&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >其中:<p >language属性表示语言选项,它与html页面上script元素的language属性极为类似,其值提供函数/方法定义所用的脚本语言。不过,language属性不是必须的,但如果指定,则它的值必须是下列语言之一:C#、VB、JScript、JavaScript、VisualBasic或CSharp。如果未指定,则默认语言为JScript。与其他Xslt元素及属性严格区分大小写不同的是,这里的语言名称不区分大小写,因此,"JavaScript"和"javascript"是等效的。<p >implements-prefix属性是强制的。用于指定命名空间前缀并将其与脚本块关联。属性的值表示命名空间前缀。前缀所代表的命名空间必须在样式表中的某个位置定义。 <p >同其他任何Xml元素标记一样,<ccid_code>&lt;msxsl:script&gt;</ccid_code>元素中的"msxsl"表示命名空间前缀。不过,名字"msxsl"并不重要,也就是说,并不一定非是"msxsl"不可,你一样可以命名为其他的字符串,比如:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>xmlns:myxsl=&quot;urn:schemas-microsoft-com:xslt&quot; xmlns:myns=&quot;urn:schemas-microsoft-com:xslt&quot;</ccid_code></pre></td></tr></table></ccid_nobr><p >这样,命名空间前缀变为了"myxsl"或者"myns",相应地,元素<ccid_code>&lt;msxsl:script&gt;</ccid_code>就将改为<ccid_code>&lt;myxsl:script&gt;或&lt;myns:script&gt;</ccid_code>。这种改变不会对结果有什么影响,如果你在你的xslt文档所有部分保持一致的话。(在本文中,所有出现<ccid_code>&lt;script&gt;</ccid_code>元素的地方使用了msxsl,是为了与微软默认设置保持一致)<p >不知你是否已经注意到,上面的语句中,不管命名空间前缀怎样改变,其值却始终是urn:schemas-microsoft-com:xslt。这是必须的。要正确扩展xslt样式表功能,就必须指定这个命名空间。命名空间urn:schemas-microsoft-com:xslt符合W3C推荐标准,并在微软MSXML3.0以上处理器中提供支持。使用它不仅能在xslt中进行嵌入脚本编程,还能对其他扩展功能提供支持。比如,在xsl中使用node-set()函数。<p >通常,在<ccid_code>&lt;xsl:stylesheet&gt;元素内定义&lt;msxsl:script&gt;元素,然后在&lt;msxsl:script&gt;</ccid_code>元素内定义扩展函数,还可以在<ccid_code>&lt;msxsl:script&gt;</ccid_code>中实例化COM对象,这将大大扩展纯Xslt的功能。(不过,用户的安全设置或许会阻止你的脚本实例化客户端对象)<p >扩展函数包含在<ccid_code>&lt;msmsl:script&gt;</ccid_code>元素所定义的脚本块中。样式表可包含多个脚本块,各脚本块的操作相互独立。也就是说,如果在脚本块的内部执行,则无法调用在其他脚本块中定义的函数,除非该脚本块声明为具有同一命名空间和同一脚本语言。由于每个脚本块都可以使用自己的语言,因此脚本块的分析将遵照该语言分析器的语法规则进行。使用的语法对于所用的语言而言必须是正确的。例如,如果使用的是 C# 脚本块,则在该块中使用 XML 注释节点 <ccid_code>&lt;!-- an XML comment --&gt;</ccid_code> 是错误的,应该使用C#注释符"//"或者"/*…*/"。<p >下面的例子建立一个附带名字空间前缀"mycompany"的脚本块,脚本中包含一个"sum"的函数,该函数接受两个double类型参数,并计算它们的和,然后转换成字符串输出。随后,xsl元素<ccid_code>&lt;xsl:value-of&gt;</ccid_code>的select属性调用这个函数并输出结果。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;xsl:stylesheet xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;                xmlns:msxsl=&quot;urn:schemas-microsoft-com:xslt&quot;                xmlns:mycompany=&quot;http://mycompany.com/mynamespace&quot;                version=&quot;1.0&quot;&gt;  &lt;msxsl:script language=&quot;c#&quot; implements-prefix=&quot;mycompany&quot;&gt;    private string sum(double a,double b) {      return (a+b).ToString();    }  &lt;/msxsl:script&gt;  &lt;xsl:template match=&quot;/&quot;&gt;    &lt;xsl:value-of select=&quot;mycompany:sum(5.0,10.0)&quot;/&gt;  &lt;/xsl:template&gt;&lt;/xsl:stylesheet&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >为演示效果,在<ccid_code>&lt;xsl:value-of select=&quot;mycompany:sum(5,10)&quot; /&gt;</ccid_code>中调用扩展函数sum时,硬性编码了两个double值5.0和10.0,实际中,参数取值应该来自于xml源文档或者通过自定义参数传入。<p >鉴于xml元素内容的特殊要求,即它不能直接包含"、'、<、>、&等字符以及所有非显示字符,而给定语言的运算符、标识符或分隔符恰好可能包含这些字符,它们有可能被错误地解释为XML。而将所有这些字符完全替换成对应的实体(标准实体和字符实体)引用将降低xml文件的可读性。所以,在使用 msxsl:script 元素时,强烈建议无论使用何种语言,都将脚本放置在 CDATA 节内。例如,下面的 XML 显示放置代码的 CDATA 节的模板。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;msxsl:script implements-prefix='myScript' language='c#'&gt;    &lt;![CDATA[    &lt;!--在这里放置代码--&gt;    ]]&gt;&lt;/msxsl:script&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >例如,下面的示例显示如何在脚本中使用逻辑 AND 运算符。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;msxsl:script implements-prefix='myScript' language='c#'&gt;    public string book(string abc, string xyz)    {  if ((abc== abc)&amp;&amp;(abc== xyz)) return bar+xyz;        else return null;    }&lt;/msxsl:script&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >由于"&"符没有转义,因此这将引发异常。将文档作为 XML 加载,并且不对 msxsl:script 元素标记之间的文本运用任何特殊处理。<p ><center><font color="#000099"><strong>.Net中使用带扩展功能的Xslt样式表</strong></font></center><p >.Net中的Xslt转换对象提供了对在Xslt文档中用脚本扩展元素来嵌入脚本语言的支持。XslTransform类专门为此提供实现。它包含的方法使用 Xslt样式表转换 Xml 数据为指定的输出。其中,用于载入Xslt转换文档的XslTransform..Load()方法接受包含嵌入脚本的样式表,实际执行转换的XslTransform.Transform()方法使用这个样式表将Xml源文档转换为指定的输出。在这里,源Xsl文档是否包含嵌入脚本并不影响程序代码的编写,你不需要为此额外添加代码。也就是说,在高级语言中编写程序代码时,包含嵌入脚本的Xsl文档与不包含嵌入脚本的Xsl文档使用一样的转换语句。<p >XslTransform类支持使用<ccid_code>&lt;msxsl:script&gt;</ccid_code>元素的嵌入脚本撰写。加载样式表时,任何已定义的函数均通过包装在类定义中被编译为Microsoft中间语言(MSIL),因此不会有任何性能损失。<p >综合上面的知识,下面使用Visual Studio.Net SDK创建了一个控制台应用程序UseXsltScript,该程序调用包含嵌入脚本的trans.xsl转换文档,对data.xml源文档执行转换,并将结果输出到一个新的newxml.xml文件。该例中的Xslt嵌入脚本用来计算已知三角形底边及该边对应高度的情况下,计算此三角形的面积。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>using System;using System.Xml;using System.Xml.Xsl;using System.IO;namespace MyCompanyUri.UseXsltScript{        class Xslt        {                private const string xmlfile = &quot;../../data.xml&quot;;//xml源文档                private const string xslfile = &quot;../../trans.xsl&quot;;//xsl转换文档                private const string newxml=&quot;../../outxml.xml&quot;;//转换后的xml文档                public static void Main()                 {                        XmlDocument doc=new XmlDocument();//实例化xml文档对象                        doc.Load(xmlfile);//加载xml文档                        XslTransform xslt = new XslTransform();        //实例化XsltTransform转换类对象                        xslt.Load(xslfile);//加载xslt转换样式表                        FileStream myStream=new FileStream(newxml,FileMode.OpenOrCreate);//以流方式打开文件                        XmlTextWriter writer = new XmlTextWriter(myStream,null);//实例化xml编写器并提供控制台输出                        writer.Formatting = Formatting.Indented;//设置输出的缩进格式                        xslt.Transform(doc, null, writer);//转换xml文档并输出                        writer.Close();                }        }}</ccid_code></pre></td></tr></table></ccid_nobr><p >xml源文件data.xml:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;triangles&gt;        &lt;triangle&gt;                &lt;size&gt;                        &lt;width&gt;2&lt;/width&gt;                        &lt;height&gt;4&lt;/height&gt;                &lt;/size&gt;        &lt;/triangle&gt;        &lt;triangle&gt;                &lt;size&gt;                        &lt;width&gt;13&lt;/width&gt;                        &lt;height&gt;8&lt;/height&gt;                &lt;/size&gt;        &lt;/triangle&gt;&lt;/triangles&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >trans.xsl文件:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot; xmlns:msxsl=&quot;urn:schemas-microsoft-com:xslt&quot; xmlns:myscript=&quot;urn:myns&quot;&gt;        &lt;msxsl:script language=&quot;C#&quot; implements-prefix=&quot;myscript&quot;&gt;     &lt;![CDATA[     public double triangleArea(double width,double height){       double area = width*height/2;//面积公式:(底*高)/2       return area;//返回三角形面积     }      ]]&gt;   &lt;/msxsl:script&gt;        &lt;xsl:template match=&quot;//triangles&quot;&gt;                &lt;triangles&gt;                        &lt;xsl:for-each select=&quot;triangle/size&quot;&gt;&lt;!--选定size节点--&gt;                                &lt;triangle&gt;                                        &lt;xsl:copy-of select=&quot;../size&quot; /&gt;&lt;!--复制size节点 --&gt;                                        &lt;area&gt;                                                &lt;xsl:value-of select=&quot;myscript:triangleArea(width,height)&quot; /&gt;&lt;!--调用函数--&gt;                                        &lt;/area&gt;                                &lt;/triangle&gt;                        &lt;/xsl:for-each&gt;                &lt;/triangles&gt;        &lt;/xsl:template&gt;&lt;/xsl:stylesheet&gt;</ccid_code></pre></td></tr></table></ccid_nobr><p >执行这个控制台程序,在应用程序目录的相应地方将生成outxml.xml文件。它将包含给定三角形的底边、高度尺寸及对应的面积。<p >另外,运行这个应用程序的时候你能够明显感觉到程序产生了短暂的延迟。这是因为在装入样式表处理嵌入式脚本之前,必须先装入c#编译器并运行该嵌入式c#脚本,这将花费2秒左右的时间。<p >需要注意的是:Xslt嵌入脚本中扩展函数的参数及函数返回值必须是 W3C XPath 类型之一,这些类型并不与.Net所支持的类型完全一致(关于这些W3C Xpath类型和.Net类型的对应情况请参见MSDN)。如果脚本函数使用其他的类型,或者,如果函数在样式表加载到 XslTransform 对象中时不进行编译,则将引发异常。<p >(责任编辑 <ccid_nobr>Sunny</ccid_nobr>)                                 <p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-10 13:15 , Processed in 0.382930 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表