新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论XSL,XSLT,XSL-FO,CSS等技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - XML技术『 XSL/XSLT/XSL-FO/CSS 』 → 增强 XSL 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 2500 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 增强 XSL 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     diy930 帅哥哟,离线,有人找我吗?双鱼座1979-3-8
      
      
      威望:4
      头衔:用脑专家
      等级:大三暑假(TOFEL考了650分!)
      文章:50
      积分:930
      门派:XML.ORG.CN
      注册:2004/7/12

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给diy930发送一个短消息 把diy930加入好友 查看diy930的个人资料 搜索diy930在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 引用回复这个贴子 回复这个贴子 查看diy930的博客楼主
    发贴心情 增强 XSL

    增强 XSL

    作者:Kurt Cagle

    更新日期:2000 年 4 月 5 日

    下载 Enhancingxsl.exe

    摘要:XSL 提供一种针对 XML 数据执行复杂转换的方法。 通过结合使用模板和脚本,可以大大增强这些转换,以便执行精确的验证和查询、合并下级文档并从任何数据生成极其复杂的应用程序。(36 页打印页)

    在本文中,我将介绍 XSL 的基本结构,并重点介绍 XSL 如何使用模板机制将特定节点与输出匹配。 我还将指出 XSL 机制自身存在的一些局限性,这些局限性可通过外部机制来克服。

    注文本中的示例是从本文随附的示例中提取的。 您可以在阅读有关特定示例的讨论时下载这些示例并查看相应的示例文件。 要运行这些示例,请打开 Displaytransforms.html 并单击相应的链接。

    内容

    本页内容
    增强 XSL
    合并脚本
    使用正则表达式
    调用 Microsoft ActiveX 控件
    XSL-T(转换)
    使用参数
    使对象成为 XSL 友好对象
    小结

    增强 XSL
    下面是您面对的一些难题: XSL 和 DNA 有哪些共同之处? 我现在要讨论的不是 Microsoft? Windows? DNA,尽管 XML 将要成为它的一个集成部分。 我在这里要讨论其他类型的 DNA — 脱氧核糖核酸,我们和地球上的所有其他生命都是由它构造而成的。

    要放弃吗? 答案实际上相当简单 — 二者都是匹配模板的示例。 DNA 通过提供漂浮在需要匹配的线粒体周围的氨基酸形状序列来工作。 您可以按照 DNA(实际上是按照 RNA — DNA 的对应物)所公开的模式将足够多的氨基酸放在一起,并且开始开发对于生命来说为必需物质的大型的、复杂的蛋白质。 只有能够适当匹配模板的氨基酸才能放入适当的空间,尽管有可能创造不同于这些氨基酸但仍适合放入适当空间的与之相似的分子,但是会出现转录错误和突变。

    此方法的好处在于,进程基本上是发生在本地的 — 蛋白质在确定眼睛颜色的位置生成,例如,不必关心染色体进行到一半时发生的事情。 换句话说,DNA(从技术上讲,是蛋白质的 RNA 转录)是大规模并行计算的示例;蛋白质序列不是逐一形成的,而是同时在整个 DNA 模板上形成的。

    可扩展的样式表语言 (XSL) 的工作方式与之类似。 在 XSL 转换中,您将发现许多可以与特定的可扩展标记语言 (XML) 模式相匹配的模板。 这些模板对于它们所处理的特定数据没有固有的序列概念和预先形成的概念 — 它们在设计上只是与特定的模式相匹配。 与传统的编程子例程相比(其中发生一组相当不同的处理步骤),XSL 模板能够以想像得出的任何顺序出现。 与 RNA 转录一样,它们基本上也是本地的。

    这并不是说可以无限类比。 这两个进程的主要区别在于,DNA 是真正并行的,而 XSL 通常按顺序工作。 在 XSL 模板中,对于所确定的匹配项有一个排序顺序。 假设大多数计算机程序目前都是具有一定顺序的图灵机,那么这实际上不是很大的局限。 相反,有可能创建用 DNA(至少是在蛋白质生成级别)的确无法构造的递归结构。

    实际上,一些功能最强大的 XSL 转换也是最简单的、最小的“线性”转换。 请考虑下面的 XML 结构,这是来自虚构公司的雇员列表。 (我打算只显示所关注的区域中的一些雇员,但是本文随附的示例中总共包含 20 名雇员。)

    <!-- msdn-employees1.xml -->
    <employees>
       <employee>
          <id>101</id>
          <firstName>John</firstName>
          <lastName>Coake</lastName>
          <title>President</title>
          <dateStarted>1997-11-12</dateStarted>
          <salary>324021</salary>
          <department>Administration</department>
       </employee>
       <employee>
          <id>102</id>
          <firstName>Lisa</firstName>
          <lastName>Jacobson</lastName>
          <title>Chief Executive Officer</title>
          <dateStarted>1997-08-12</dateStarted>
          <salary>329215</salary>
          <department>Administration</department>
       </employee>
       <employee>
          <id>103</id>
          <firstName>Carolyn</firstName>
          <lastName>Seeley</lastName>
          <title>Chief Financial Officer</title>
          <dateStarted>1998-03-16</dateStarted>
          <salary>232768</salary>
          <department>Finance</department>
       </employee>
       <employee>
          <id>104</id>
          <firstName>Amy</firstName>
          <lastName>Anderson</lastName>
          <title>Chief Technical Officer</title>
          <dateStarted>1998-09-16</dateStarted>
          <salary>242768</salary>
          <department>Information Technologies</department>
       </employee>
       <employee>
          <id>105</id>
          <firstName>Frank</firstName>
          <lastName>Miller</lastName>
          <title>Vice President, Marketing</title>
          <dateStarted>1998-11-03</dateStarted>
          <salary>210359</salary>
          <department>Marketing</department>
       </employee>
       <employee>
          <id>106</id>
          <firstName>Mike</firstName>
          <lastName>Seamans</lastName>
          <title>Senior Analyst</title>
          <dateStarted>1999-03-16</dateStarted>
          <salary>160204</salary>
          <department>Information Technologies</department>
       </employee>
          <!-- More employees follow -->
    </employees>

    注如果您运行的是 Microsoft Internet Explorer 5.0,那么您实际上可以通过使用 XML 开头处的处理指令,将 XSL 文档作为样式表应用于该文档,从而直接看到该转换:

    <?xml:stylesheet type="text/xsl" href="yourTransform.xsl"?>

    为了使用 Microsoft XML 分析器技术预览版 (MSXML2) 执行 XSLT 转换,您需要编写一个脚本代码,以便通过使用 XML DOM 文档的 transformNode或 transformNodeToObject 方法来应用模板。 transformNode 方法将 XSL 文档用作它的唯一参数,而 transformNodeToObject 使用 XSL 文档对象和一个目标文档来接收结果,如下所示:

    Dim xmlDoc   ' as DOMDocument
    Dim xslDoc   ' as DOMDocument
    Dim targetDoc   ' as DOMDocument
    Dim rslt     ' as String
    Set xmlDoc=CreateObject("Microsoft.XMLDOM")
    Set xslDoc=CreateObject("Microsoft.XMLDOM")
    Set targetDoc=CreateObject("Microsoft.XMLDOM")
    XmlDoc.load "yourXMLDoc.xml"
    XSLDoc.load "yourTransform.xsl"
    ' To output to a text stream
    Rslt=xmlDoc.transformNode(xslDoc)
    ' To output to a receiving XML object
    xmlDoc.transformNodeToObject xslDoc,targetDoc

    在所有的 XSL 脚本中,最简单的或许就是标识转换。 这种转换将 XML 文档作为输入,并将同一个文档作为输出来返回。 它可能不是特别有趣,但是它却是几乎所有的、具有任何功能的 XSL 脚本的基础。

    注除非另行说明,否则,我将使用由 “http://www.w3.org/TR/WD-xsl” 命名空间表示的旧的 Microsoft 1998 XSL 规范。 较新的 XSL 命名空间包含一些令人兴奋的功能,我将在以后介绍它们,目前让我们先了解一下基础知识。

    <!-- identityTransform.xsl -->
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
       <!-- Match the root node -->
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <!-- Match everything else -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
    select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    这种转换看上去有点神秘,但是可将它分为两项任务: 提供一个与第一个节点匹配的模板;然后找出与所有其他节点匹配的模板。 这两项任务都需要 XSL 分析器的一个工件 — 它需要具有一个专门指定的、用来开始操作的匹配项。 模式 “/” 与文档的根节点(这与文档的第一个节点不是一回事)相匹配。 (同时,您需要这样一种功能,即不仅能够保存 XML 结构,还能够保存所有的处理指令、注释或其他可能出现在“传统”XML 对象外部的资源。) 文档元素(即根节点的子级)是指文档中的第一个“可见”节点。

    第一个模板又命令 XSL 分析器在 XSL 文档中查找与根节点的子元素相匹配的任何模式(由通配符 “*” 来表示)。 处理器随后从 XSL 文档末尾开始反向工作(请记住此顺序 — 它非常重要),直到遇到一个与之匹配的模板模式。 在本例中,它是不易使用的模式 "*|@*|text()|cdata()| comment()|pi()"。 简单地说,那是一切别的东西 — 元素、属性、文本节点、CDATA 节、注释和处理指令。 该模板随后复制应用的所有子节点(此处也可以是其他的元素)并应用同一个模板,以便在树的下一层捕捉所有这些元素。

    这两个模式最终都以递归方式工作,并且可以在非常短的时间内处理 XML 树。 当然,假设您在开始的地方结束,这种高效性似乎会被浪费。 但是,在处理此类排列时有一个诀窍,那就是记住处理器将从末尾处开始查找它遇到的第一个匹配项,因此您可以在保留旧结构的同时对元素进行更改。

    在开始时,假设贵公司进行过重组,“Information Technologies” 部门已被更名为 “Research” 部门。 在 XML 文档中,可以再向标识转换中添加一个筛选器,以便更改部门的名称,如下所示:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
       <!-- Match the root node -->
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <!-- Match everything else -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
    select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <!-- Match Information Technologies -->
       <xsl:template match="department[.='Information Technologies']">
          <xsl:copy>Research</xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    如果您尝试对另一个架构执行 XSL 转换,这可能非常有用。 例如,假设您希望在重组过程中按以下两种方式更改架构: 您希望添加一个能够反映部门名称的部门代码;而且您希望添加一个从 1 到 10 的评分代码,以便指出人员的业绩评分,并用它来批准加薪或者设定纪律处分标记(以及研究名称的更改)。 再对 XSL 进行一点修改,将通用的规则放在离开头更近的位置,将越专门的异常放在离结尾更近的位置,如下例所示:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
       <!-- Match the root node -->
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <!-- Handle any node not yet matched -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <!-- For the general employee, add a rating of 5 -->
       <xsl:template match="employee">
          <xsl:copy>
          <xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/>
          <rating>5</rating>
          </xsl:copy>
       </xsl:template>
       <!-- Match the general department and the departmentID -->
       <xsl:template match="department">
          <xsl:copy><xsl:value-of/></xsl:copy>
          <departmentID><xsl:choose>
             <xsl:when test=".[.='Administration']">ADM</xsl:when>
             <xsl:when test=".[.='Marketing']">MRK</xsl:when>
             <xsl:when test=".[.='Finance']">FIN</xsl:when>
             <xsl:when test=".[.='Information
             Technologies']">RSC</xsl:when>
          </xsl:choose></departmentID>
       </xsl:template>
       <!-- Match the Information Technologies department, and change the
          name of the department -->
       <xsl:template match="department[.='Information Technologies']">
          <xsl:copy>Research</xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    结果显示在 msdn-employees3.xml 中,如下所示:

    <!-- msdn-employees3.xml -->
    <employees>
       <employee>
          <id>101</id>
          <firstName>John</firstName>
          <lastName>Coake</lastName>
          <title>President</title>
          <dateStarted>1997-11-12</dateStarted>
          <salary>324021</salary>
          <department>Administration</department>
          <departmentID>ADM</departmentID>
          <rating>5</rating>
       </employee>
       <employee>
          <id>102</id>
          <firstName>Lisa</firstName>
          <lastName>Jacobson</lastName>
          <title>Chief Executive Officer</title>
          <dateStarted>1997-08-12</dateStarted>
          <salary>329215</salary>
          <department>Administration</department>
          <departmentID>ADM</departmentID>
          <rating>5</rating>
       </employee>
       <employee>
          <id>103</id>
          <firstName>Carolyn</firstName>
          <lastName>Seeley</lastName>
          <title>Chief Financial Officer</title>
          <dateStarted>1998-03-16</dateStarted>
          <salary>232768</salary>
          <department>Finance</department>
          <departmentID>FIN</departmentID>
          <rating>5</rating>
       </employee>
       <employee>
          <id>104</id>
          <firstName>Amy</firstName>
          <lastName>Anderson</lastName>
          <title>Chief Technical Officer</title>
          <dateStarted>1998-09-16</dateStarted>
          <salary>242768</salary>
          <department>Research</department>
          <departmentID>RSC</departmentID>
          <rating>5</rating>
       </employee>
       <employee>
          <id>105</id>
          <firstName>Frank</firstName>
          <lastName>Miller</lastName>
          <title>Vice President, Marketing</title>
          <dateStarted>1998-11-03</dateStarted>
          <salary>210359</salary>
          <department>Marketing</department>
          <departmentID>MRK</departmentID>
          <rating>5</rating>
       </employee>
       <employee>
          <id>106</id>
          <firstName>Mike</firstName>
          <lastName>Seamans</lastName>
          <title>Senior Analyst</title>
          <dateStarted>1999-03-16</dateStarted>
          <salary>160204</salary>
          <department>Research</department>
          <departmentID>RSC</departmentID>
          <rating>5</rating>
       </employee>
          <!-- More employees follow -->
    </employees>

    请注意,这里有几处比较微妙。 对于初学者来说,尽管 <rating> 标记很早就在 XSL 脚本中应用,但这是于通用的 <xsl:apply-templates> 标记之后发生的。 因此,评分位于列表的末尾处。 因为这些调用通常都以递归方式进行,所以实现特定的顺序可能会需要一些工作。

    除此之外,还要注意顺序如何影响被调用的模板。 如果专门捕捉 “Information Technologies” 部门的模板已经出现在通用的部门模板的前面,则它就永远不会被调用。 了解此规则或许是处理 XSL 的一个重要方面。

    返回页首
    合并脚本
    向 XSL 中添加脚本功能会在牺牲可移植性的同时增强其有效性。 然而,有许多转换不使用脚本几乎是无法实现的。 本节将探讨将脚本节点合并到 XSL 转换的机制、<expr> 标记的功能和缺点、<xsl:if> 和 <xsl:when> 标记中的表达式使用。 我还将介绍在不同的范围级别维护状态变量,以及控制如何设置 <expr> 对象的输出,以便它可以生成 XML 形式的、独立于源 XML 文档的输出。

    对于可能无法总是通过简单匹配解决的脚本,上一节中介绍的评分系统增加了其复杂性。 例如,假设显示在 Msdn-employees3.xml 中的架构已经创建了一会儿,经理(或者高级主管、股东)将定期评估评分并按照业绩加薪或降薪。 特别是,薪水将按照下表所示的那样增加或减少。

    而且,如果评分等于或小于 3,雇员会被置于试用状态,并在其记录中收到一个 <probation>Warning</probation> 标记。 如果某个雇员已经有了一个 <probation> 标记,该标记将被设置为 <probation>Terminate</probation>,而且该雇员将收到一张解雇通知书。 (这是一个特别严厉的公司。)

    旧的 XSL 模式匹配语法可以识别不同的评分,但是新薪金却以一种完全不同的方式进行计算。 但是,如果您借助于一些 Microsoft 扩展,则可以通过几种不同的方式来达到此目的。 最简单的机制(尽管是代码量最大的一个)是 <xsl:eval> 标记。 此标记用给定的脚本语言(JavaScript 或 Microsoft Visual Basic? Scripting Edition (VBScript))对表达式进行求值并返回一个结果。 通常,<xsl:eval> 节点允许您生成 XSL 本身通常不提供的数据。 因此,要测试评分是否等于或小于 3,可以编写下面的 XSL 脚本:

    <!-- Performance1.xsl -->
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="JavaScript">
       <!--Match the root node -->
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <!-- Match everything else -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <xsl:template match="rating">
          <xsl:copy><xsl:value-of/></xsl:copy>
          <xsl:if test=".[. $lt$ 4]">
             <xsl:choose>
                <xsl:when
                test="ancestor(employee)/probation[.='Warning']">
                   <probation>Terminate</probation>
                </xsl:when>
                <xsl:otherwise>
                   <probation>Warning</probation>
                </xsl:otherwise>
             </xsl:choose>
          </xsl:if>
       </xsl:template>
       <xsl:template match="probation"/>
       <xsl:template match="salary">
          <xsl:copy>
          <xsl:choose>
             <xsl:when test="ancestor(employee)/rating[.=10]">
    <xsl:eval>this.nodeTypedValue*( 1.20)</xsl:eval>
             </xsl:when>
             <xsl:when test="ancestor(employee)/rating[.=9]">
    <xsl:eval>this.nodeTypedValue*( 1.15)</xsl:eval>
             </xsl:when>
             <xsl:when test="ancestor(employee)/rating[.=8]">
    <xsl:eval>this.nodeTypedValue*( 1.10)</xsl:eval>
             </xsl:when>
             <xsl:when test="ancestor(employee)/rating[.=7]">
    <xsl:eval>this.nodeTypedValue*( 1.05)</xsl:eval>
             </xsl:when>
             <xsl:when test="ancestor(employee)/rating[.=3
             $or$ .=2]">
    <xsl:eval>this.nodeTypedValue*( 0.95)</xsl:eval>
             </xsl:when>
             <xsl:when test="ancestor(employee)/rating[.=1]">
    <xsl:eval>this.nodeTypedValue*( 0.90)</xsl:eval>
             </xsl:when>
             <xsl:otherwise>
                <xsl:value-of/>
             </xsl:otherwise>
          </xsl:choose>
          </xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    尽管这里有许多事情要做,但是请暂时将精力放在薪金匹配模板上。 这将举例说明如何利用 <xsl:choose><xsl:when/><xsl:otherwise/></xsl:choose> 结构。 除非评分为 4、5 或 6,否则,每个条件都包含在一个 <xsl:when>容器中。在评分为 4、5 或 6 时,默认的 <xsl:otherwise> 块只是将当前的薪金传递回输出流。 对于所有其他情况,<xsl:eval> 块用于暂时跳入脚本世界,以便计算新薪金。

    在该脚本中,this的含义可能会有点令人困惑。在此处显示的情况下,this是指当前的节点,它公开从XML DOM 继承 IXMLDOMNode 接口的接口。 为了实现较好的效果,使用 this 来检索薪金的类型化值。 在没有架构的情况下,如果文本是数值,则此值会自动转换为数值,否则会被作为字符串进行处理。 此值随后会乘以相应的百分比,并在插入到输出流之前隐式转换为流。 <xsl:copy> 包装块确保该元素被复制到 <salary> 标记中。

    我包括了警告操作,以便清楚地说明 <xsl:if> 和 <xsl:choose> 结构之间的区别。 在 XSL 中,if 块只检查一个条件 — 如果该条件为真,它就求值;否则,它就不求值。如果您需要将代码有条件地插入到输出流中(例如,当评分小于 4 时),这非常有用。

    另一方面,<xsl:choose> 结构维护状态并允许您在多个可能的选项之间进行选择。 如果您可能会在 Visual Basic 中使用 select关键字或者在 Java 中使用 switch,则这是个非常有用的结构。 它还是处理 if then else 语句的首选方法,因为以下代码

    <xsl:choose>
    <xsl:when test="condition>resultA</xsl:when>
    <xsl:otherwise>resultA</xsl:otherwise>
    </xsl:choose>

    实质上与下面的代码相同

    if condition
       then resultA
       else resultB
    end if

    您可以针对上面的 XSL 脚本设置非常引人注目的参数 — 尽管这十分有效,但是它看上去过于复杂、冗余和冗长。 如果您可以为了维护目的而简化此代码,它将十分有用。 这可以通过在旧式 XSL 浏览器中,用 XSL 的另一个 Microsoft 扩展(即,<xsl:script> 标记)来完成。 这个特殊的标记允许您在代码中放置复杂的脚本块,以便简化代码并用来扩展 XSL 功能。

    但是,在开始讨论脚本编写之前,我想讨论一下语言范围。 通常,XSL 脚本的默认语言是 JavaScript。 (实际上,该语言恰好就是系统本身使用的默认脚本,但是几乎没有人会更改默认语言 JavaScript,因此,除非您显式更改它,否则可能还得继续编写 JavaScript。) 但是,您可以将 language 属性添加到几乎所有的 XSL 标记中,而且在该标记的范围中将使用这种语言。

    通常,最好指定一种要用在 XSL 文档中的语言并一直使用它。 每当您指出另一种语言时,XSL 分析器都会将该语言加载到内存中,这会极大降低性能。 而且,语言间调用通常的系统开销比语言内调用大得多。

    因此,仅仅为了区别起见,让我们假设您决定用 VBScript 编写脚本并希望通过 getNewSalary 函数来输出结果。 下例说明如何编写这样的 XSL 脚本:

    <!-- Update Salary -->
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="VBScript">
       <xsl:script><![CDATA[
    function getNewSalary()
       dim rating
       dim adjustmentArray
       adjustmentArray=Array(.90,.95,.95,1.00,1.00,1.00,1.05,1.10,1.15,1.20)
       rating=me.selectSingleNode("ancestor(employee)/rating").nodeTypedValue
       getNewSalary=rating*me.dataTypedNode*adjustmentArray(rating-1)
    end function
    ]]></xsl:script>
       <!--Match the root node -->
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <!-- Match everything else -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <xsl:template match="rating">
          <xsl:copy><xsl:value-of/></xsl:copy>
          <xsl:if test=".[. $lt$ 4]">
             <xsl:choose>
                <xsl:when
                test="ancestor(employee)/probation[.='Warning']">
                   <probation>Terminate</probation>
                </xsl:when>
                <xsl:otherwise>
                   <probation>Warning</probation>
                </xsl:otherwise>
             </xsl:choose>
          </xsl:if>
       </xsl:template>
       <xsl:template match="probation"/>
       <xsl:template match="salary">
          <xsl:copy><xsl:eval>getNewSalary</xsl:eval></xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    <xsl:script> 节点的开头和结尾都是 CDATA 声明 (<![CDATA[ ]]>),这样可确保在内部预留空白,而且诸如小于 (<) 或大于 (>) 之类的符号不会被误解为 XML 分隔符。

    任何事情都阻挡不了您在一个文档中设置多个脚本节点或者在同一个文档中包含多个函数。 的确,如果您拥有大脚本或者大量函数,可能会发现按这种方式将它们分开会非常好。 通常,<xsl:script> 节点会在处理器分析模板之前自动加载到内存中。 而且,如果您在函数外部的 <xsl:script> 节点中有一个语句,则该语句将在模板被分析之前自动求值,这意味着您可以将初始化代码放在这些脚本节点中,并期望它将在任何处理实际发生之前运行。

    在上面的脚本中,您可能已经发现没有传递任何参数。 这不是因为它们不工作。 (它们能够工作,而且它们是按照您声明任何 VBScript 参数的方法来声明的。) 在这个特殊情况下,之所以没有传递任何参数,是因为不存在 me 对象。 我们在上面讨论 this 对象时遇到过这个对象 — 在 VBScript 中,me 包含当前的上下文。 当您从 <xsl:eval> 标记进行调用时,me 包含最新的上下文,而且该上下文会继续进入被调用的任何函数中。 因为 me 包含对当前薪金节点的引用,所以,检索薪金会非常简单 (me.nodeTypedValue),而且获取对同辈评分节点的引用几乎同样简单:

    (me.selectSingleNode("ancestor(employee)/rating"))

    我想指出上例中的另一点,请关注下面的仅包含一行代码的模板:

       <xsl:template match="probation"/>

    这不是拼写错误。 当您使用标识转换时,有时会希望从 XML 文档中删除标记。 在本例中,我想在没有任何标记存在且条件为真(即,如果评分小于 4)时添加一个 <probation>Warning</probation> 标记。 但是,如果业绩评分在一个评估期间未得到改善,该标记的内容将需要更改为 <probation>Terminate</probation>。 在 XSL 中,用来同时处理节点创建和节点更改的代码可能会非常多,因此,如果不传递旧的节点值,而只是在以后创建一个新节点,会更加方便。 请注意,这会产生意料之外但是令人满意的结果: 如果某个人的业绩确实得到改善,则 probation 标记将不会进行传递,因此您不必显式排除它。

    XSL 模型的 Microsoft 扩展还包括另一个属性:xsl:eval 属性。 该属性可应用于 <xsl:if> 和 <xsl:when> 标记,用来替换或增加 test 属性。 例如,假设您希望检索所有姓氏以 “J” 的开头的雇员姓名列表。 在本例中,确实不需要创建一个标识转换,为变化起见,我将该过程引申了一步,并随意假设此输出将是项目符号列表形式的 HTML,如下例所示:

    <!-- showJEmployees.xsl Show Employees starting with J -->
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="VBScript">
       <xsl:template match="/">
          <ol>
          <xsl:apply-templates select="//employee"/>
          </ol>
       </xsl:template>
       <!-- Match employees -->
       <xsl:template match="employee">
          <xsl:apply-templates select="lastName"/>
       </xsl:template>
       <xsl:template match="lastName">
    <xsl:if expr='left(me.text,1)="J"'>
          <li>
    <xsl:value-of select="ancestor(employee)/firstName"/>
    <xsl:value-of/>
       </li>
    </xsl:if>
       </xsl:template>
    </xsl:stylesheet>

    在这里,顶层的匹配节点将 HTML 排序的列表标记 (<ol>) 放在输出块周围,以便姓名列表被编号。 该模板对雇员节点进行匹配,以便节点集(或者符合该模板的那些元素或属性)由整个雇员集合组成。 这反过来会对每个雇员节点树中的所有 lastName节点进行调用。 (请注意,您还可以非常方便地直接指定 lastName 节点,但是,如果 XML 结构利用雇员节点上下文外部的 <lastName>,则这些也可能已被选取。)

    与 <xsl:eval> 标记一样,表达式属性 xsl:expr 依赖最近定义的脚本语言 — 在本例中为 VBScript。 因为我是在表达式字符串中对字符串进行比较,所以,尽管表达式中的双引号也将是可以接受的、但不太易读的替代符号,但我还是切换到了 XML 的替换单引号。 在转换为布尔值时,<xsl:if> 和 <xsl:when> 标记只查找表达式的结果值,因此,返回零的表达式将被解释为假,而非零返回值将生成实际的值。

    还可以将 xsl:expr 和 xsl:test 属性组合到同一个 <xsl:if> 或 <xsl:when> 标记中。 这些标记的作用与“布尔和”相同。 如果表达式和测试返回的值均为 “True”,则 <xsl:if> 或 <xsl:when> 标记的内容将被传递到输出流。 否则,它们将不传递到输出流。

    请注意,与构造一个合理的查询相比,使用 <xsl:if> 或 <xsl:when> 对输出进行筛选效率较低。 给定模式的所有可能的上下文都会被检索,然后它们通过 xsl:if 或 xsl:when 筛选器再次运行,而高效的查询仅对可能元素的列表进行一次迭代通过。 但是,在许多情况下,对于此问题实际上没有任何办法 — 尽管查询语言非常高级,但是它却不能完全包含过程语言的所有可能的事件(尽管使用较新的分析器会比较方便)。

    顺便说一句,您有时可能希望让 <xsl:expr> 标记将结构化 XML(或 HTML)传递到输出流。 在默认情况下,<xsl:expr> 在输出时自动将任何 XML 转换为实体,这主要是为了防止输出尝试分析 transformNodeToObject调用中的无效 XML。 这可以通过将no-entities="true" 属性应用于 <xsl:expr> 标记来重写,如下所示:

    <xsl:expr no-entities="true">
    "<h1>This will output as <b>Structured</b> code.</h1>"
    </xsl:expr>

    返回页首
    使用正则表达式
    因为 MSXML 分析器实现中的 XML 文档和脚本引擎具有基于文本的本质,所以,可以使用正则表达式来将功能扩展到验证信息和搜索不适宜于处理 XPath 实现的信息。 本节简要介绍正则表达式,以及如何将它们合并到 XSL 文档的脚本和表达式节点中。 本节还展示这样的正则表达式如何大大减少执行许多不适用于过程脚本的任务所必需的代码量。

    为了实现较好的效果,可以将这种组合 xsl:test 和 xsl:expr 属性的能力与隐藏在 XSL 分析器中的另一种“秘密”语言一起使用。 如果您运行的是 5.0 版的脚本引擎(如果系统上装有 Internet Explorer 5.0,则几乎可以肯定脚本引擎的版本也是 5.0),则可以利用内置在 JavaScript 和 VBScript 中的正则表达式引擎。

    对于那些没有过使用正则表达式的愉快经历的人来说,它们基本上是用来匹配大型字符串中特定字符串的模板。 正则表达式的语法可能有点简洁,但它们是功能异常强大的位代码,可以处理早期 XSL 分析器的一些烦人的方面。

    例如,考虑验证问题。 您可以使用架构来执行某个数据验证,但是这样做会带来一个问题,那就是这样的验证是“全有或全无”事务 — XML 要么分析,要么不分析。而且,您可能会有一些将无法由架构识别的数据(例如,电话号码)。 幸运的是,您可以创建一个正则表达式,以便尝试匹配搜索字符串中的特定模式,随后可以根据此字符串的任何部分是否与该正则表达式相匹配来确定某个表达式是否有效。

    正则表达式可以与文本完全匹配 — 例如,正则表达式 “Information” 将与包括单词 “Information” 的任何表达式匹配。 但是,您还可使用专门的元字符来匹配特定类型或数量的字符。 例如,表达式 “J.+” 将与任何包括 “J” 以及一个或多个其他字符的字符串相匹配(句点表示任何不是换行符的字符,+ 表示可以有一个或多个上一项目)。 另外,反斜杠字符还将特定的字符指定为元字符本身 — 它们的匹配项将是特定的字符类型,而不是反斜杠后面的字母。 例如,正则表达式 “\d+” 指出一个具有一位或多位的数字。

    还可以通过使用 “^” 来指定表达式的开头位置,使用 “$” 字符来指定表达式的末尾位置。 “^” 字符自动与字符串的开头相匹配,因此,对于任何以字母 J 开头且包含多个字符的字符串,正则表达式 “^J+” 将为真。 同样,“$” 字符将与字符串的末尾相匹配,因此,表达式 “ain$” 将与字符串 “main”、“complain” 和 “We are all expected to remain” 相匹配。

    其他标点符号也可以用在正则表达式中。 花括号 {n}(其中的 n 是一个整数)指出上一个字符或表达式必须重复 n 次。 同样,符号 {n,m} 表示该字符最少重复 n 次,最多重复 m 次,而 {n,} 指向字符在何处至少重复 n 次(尽管可以重复更多次)。 因此,您可以用正则表达式 “\d{6,8}” 来指定一个介于 6 和 8 之间的数字。 如果您希望查找某个重复的标点符号,则在大多数情况下需要在该标点符号前面使用转义符 — 例如,“\.{3}” 可用来指示省略号 ("..."),而 “.{3}” 可用来指示三个非换行字符。

    正则表达式包括多种在替换字符之间进行匹配的方法。 例如,表达式 [JjKkLl] 将与 “J”、“j”、“K”、“k”、“L” 或 “l” 中的任何字符相匹配。 您可以通过提供某个范围的第一个字符和最后一个字符来匹配该范围内的字符: [A-Za-z] 将匹配任何字母字符,而 [A-Za-z0-9_] 将匹配任何字母数字字符或者下划线字符。 您还可以通过在每个条件之间放置一条竖线 ("|") 来在整个表达式之间进行选择;因此 [red|blue|yellow] 将与包括其中任一单词的任何表达式相匹配。

    这些元字符和几个其他字符显示在下面的“字母数字元字符”和“限定符和标点符号元字符”表中。

    字母数字元字符

    评分 变化的百分比
    10
    20%

    9
    15%

    8
    10%

    7
    5%

    4-6
    0%

    2-3
    -5%

    1
    -10%

    限定符和标点符号元字符

    因此,如果要查找 1-(800)-555-1212 形式的电话号码,则可以将正则表达式写成 “1-\(\d{3}\)-\d{3}-\d{4}”;或者如果要查找邮政编码,则可以将正则表达式写成 “d{5}(-\d{4})?”。 对于后者,邮政编码的后四位数字包含在括号中,以便将它们组合在一起。 问号字符随后用来指示后面的一组是可选的。

    在 JavaScript 和 VBScript 中,可通过内置在每种脚本语言中的 RegExp 对象创建正则表达式。 在拥有该对象之后,可以将 pattern属性设置为包含要匹配模式的字符串,还可以设置或重置 ignoreCase 和 global 属性,这些属性可用来在忽略大小写的情况下搜索模式,并分别搜索可以匹配的任意多个模式示例。 test 方法反过来将指示,在传递所选文本时,正则表达式是否至少与该文本中的某项内容匹配。

    为了对此进行阐释,假设您刚从 Web 收到一个包含人员姓名和地址的 XML 文档。 在将此块的内容提交到数据库中之前,您希望验证收到的信息是否有效。 下面的脚本阐释如何至少测试邮政编码的有效性:

    <contacts>
    <contact>
       <firstName>Tom</firstName>
       <lastName>Johnston</lastName>
       <street>1234 Mockingbird Lane</street>
    <city>Olympia</city>
    <state>WA</state>
    <postalcode>91501</postalcode>
    </contact>
    <contact>
       <firstName>Kim</firstName>
       <lastName>Abercrombie</lastName>
       <street>Seaweed Lane</street>
    <city>Atlantis City</city>
    <state>WA</state>
    <postalcode>985014w-223</postalcode>
    </contact>
    </contacts>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="VBScript">
    <xsl:script>
       dim postalcodeRE
          set postalcodeRE =new RegExp()
       postalcodeRE.pattern="^d{5}(-\d{4})?$"
    </xsl:script>
    <xsl:template match="/">
       <invalidBlocks>
       <xsl:apply-templates select="contact/*"/>
       </invalidBlocks>
    </xsl:template>
    <xsl:template match="*"/>
    <xsl:template match="postalcode">
       <xsl:if expr="not postalcodeRE.test(me.text)">
          <invalidBlock>
    <xsl:copy><xsl:value-of/></xsl:copy>
    <reason>Zip-code not well-formed.</reason>
          </invalidBlock>
       </xsl:if>
    </xsl:template>
    <!-- more tests -->
    </xsl:stylesheet>

    在处理之后,XSL 筛选器将浏览所有的节点,并将只返回那些格式不正确的邮政编码节点。 对于上例,这将返回第二个节点的邮政编码:

    <invalidBlocks>
       <invalidBlock>
    <postalcode>985014w-223</postalcode>
          <reason>Postal-code not well formed</reason>
       </invalidBlock>
    </invalidBlocks>

    在新型分析器中,正则表达式不太重要,因为 XSL 规范中内置了一些更常见的字符串匹配功能。 (请参阅后面标题为 “XSL-T” 的一节。) 但是,它们在验证、文本替换和“接近匹配”方面的用途使您有理由将其留在工具箱中。

    返回页首
    调用 Microsoft ActiveX 控件
    在 Microsoft? Windows? 2000 中实现的 Microsoft XML 分析器提供如下功能:从 XSL 脚本区域创建 ActiveX? 对象。 尽管限制在某些方面,但这常常提供执行如下操作的机会:在同一个 XSL 文档中使用多个 XML 流、执行业务对象验证以及连接到数据库以接收 XML。 本节介绍通过一个作为内部对象的 ADO Recordset 来执行数据库查询。

    在这里,我实际上提取了一个快速查询。 请注意,在上面的脚本中,我使用 New 运算符来实例化 RegExp 对象的实例。 这是非常有效的,因为 RegExp 对象是脚本库的一部分,但是它还会产生一些有趣的可能性。 December 1998 XSL 实现的问题之一在于,它只允许使用一个输入源(XML 文档)和一个转换源(XSL 文档)。 不过,如果您需要从另一个 XML 文档检索信息并将它与 XSL 转换中的第一个文档结合,会发生什么事情?

    如果您从 Microsoft Windows 98 或 Microsoft Windows NT? 4.0 的 Microsoft Internet Explorer 5 使用 Microsoft MSXML 分析器,则答案就是您运气不好 — 您仍可以使用 DOM 代码执行转换,但仅此而已。 不过,Windows 2000 包含一个稍微改善的 MSXML 组件,其中包括一个非常棒的功能: 您可以实例化 XSL 内部的 ActiveX 对象。 有关详细信息,请参阅 What's New in XML for Microsoft Windows 2000。

    如果您已经编写过许多脚本,则应当熟悉此操作的代码。 在本质上,您只是使用 CreateObject 调用,并传递希望在 <xsl:script> 块中创建的对象的 progID。

    例如,考虑在某个情形下,您需要基于 XML 文档中的关键字来从数据库中提取信息。 假设您的代码是显示在 Msdn-employees3.xml 中的代码,需要添加的数据(地址块)的关键字是雇员的 ID,则下面的 XSL 脚本可以通过 ADO 2.1 或更高版本与 XML 和数据库进行通讯。

    <xsl:stylesheet xmlns:html="http://www.w3.org/TR/WD-xsl">
       <xsl:script language="VBScript">
       dim conn
       dim addressRS
       function getConnection()
          dim conn
          set conn=CreateObject("ADODB.Connection")
          set addressRS=CreateObject("ADODB.Recordset")
          conn.open "PROVIDER=SQLSERVER;SERVER=Employess"
          getConnection=""
       </xsl:script>
       function getAddressRS()
          id=me.selectSingleNode("id").text
          on error resume next
          addressRS.close
          on error goto 0
          addressRS.open "SELECT * FROM EmployeeAddresses WHERE
          id='"+id+"'"
          getRS=""
       end function
       function closeAll()
          addressRS.close
          set addressRS=Nothing
          conn.close
          set conn=Nothing
          closeAll=""
       end function
       </xsl:script>
       <!-- Match the root node -->
       <xsl:template match="/">
          <xsl:eval>getConnection</xsl:eval>
          <xsl:apply-templates select="*"/>
          <xsl:eval>closeAll</xsl:eval>
       </xsl:template>
       <!-- Match everything else -->
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <!-- Match Information Technologies -->
       <xsl:template match="employee">
          <xsl:copy>
          <xsl:apply-templates select="*"/>
          <xsl:eval>GetAddressRS</xsl:eval>
          <address>
             <street><xsl:eval>addressRS("STREET")</xsl:eval></street>
             <city><xsl:eval>addressRS("CITY")</xsl:eval></city>
             <state><xsl:eval>addressRS("STATE")</xsl:eval></state>
             <postalcode><xsl:eval>addressRS("POSTALcode")</xsl:eval></postalcode>
          </address>
          </xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    这个简单的示例展示了如何将 ADO Connection 和 Recordset 对象合并到一个 XSL 文档中 — 尽管此操作的效率很低。 (实际上,我将用一组 ID 来将多个 Recordset 调用合并到一个调用中,但是实现此操作的代码会遮盖此处要出现的内容。) 我想提醒您注意的第一点是,使用 <xsl:eval> 语句进行声明。 <xsl:eval> 声明自动将任何求值结果直接放在输出流中。 因此,我使 Recordset 和 Connection 对象对于该脚本全局可用,然后让检索过它们的例程返回将不会导致输出发生任何更改的空字符串。

    该代码的其余部分相当简单。 GetConnection 连接到数据库并创建一个连接对象和一个全局记录集。 GetAddressRS 则执行查询,以便返回与给定的雇员 ID 相关联的地址记录,并将该记录放在全局定义的变量 addressRS 中。 而此变量则在雇员模板中被访问,请求的信息会直接传递到输出流。 最后,控制返回到根节点,从而显式断开连接。

    返回页首
    XSL-T(转换)
    虽然此技术非常适合已经进行了清楚定义的情形(您知道要传递的 ID,知道用来检索对象、特定的字段信息等的表),但是 XSL 的目标是创建一般对象,以便处理各种各样的可能配置。 但是,这不像 December 1998 分析器那样简单,因为该分析器缺乏一个关键部分 — 参数化。 而且,此处提供的解决方案侧重于使用一些的确不标准的、W3C 不再支持的 XML 形式。

    Microsoft 刚发布了 Microsoft XML 分析器技术预览版。 (有关详细信息,请参阅 MSDN XML Developer Center。) 此分析器承诺将符合 W3C 标准,并且仍提供将使 XML 开发人员的工作更方便的扩展。 我在看到该预览版时认为它非常吸引人。 它方便地回答了旧分析器遇到的几乎所有疑难问题;速度更快;旨在以一种可缩放的方式高效工作;具有我所见过的、用来将第三方组件与 XSL 集成在一起的最完美的解决方案之一。 我无法介绍该分析器的所有元素,但是我确实希望简单提一下直接影响已讨论内容的要点。

    就 XSL 而言,一个重要的可缩放性问题源自如下事实:在每次调用 transformNode或transformNodeToObject时,XSL 解释器都需要构造模板。 当分析器正在进行复杂的转换时,如果您的 Web 站点被反复点击,您将觉察到显著的性能下降。 但是,指示大多数 XSL 转换的一个特征是,它们的使用率实际上没有发生太大的变化。 如果您可以编译 XSL 对象并从编译后的对象应用转换,则性能会显著改善。

    这就是 Microsoft XML 小组完成的工作。 新的 DOM 文档中引进了一个 XSL Template 对象。 这个特殊对象允许您加载一个 XSL 文档,编译它,然后创建一个“处理器”,以便将编译后的 XSL 文档应用于任何新输入,而无需重新创建 XSL。 上述操作的一个含义是,您可以在服务启动时有效地缓存这些编译后的对象,而不是在每次访问页面或服务时都重新构建它。

    下面的脚本显示如何编译原始模板并随后以编译过的形式使用。 (这是为了输出到服务器端 ASP 上。)

    <%@script language="JavaScript"%>
    <%
    // Note that these ProgIDs may change
    var xmlDoc=new ActiveXObject("MSXML2.FreeThreadedDOMDocument")
    var xslDoc=new ActiveXObject("MSXML2.FreeThreadedDOMDocument")
    xmlDoc.load(server.mapPath("Msdn-employees3.xml"))
    xslDoc.load(server.mapPath("filterEmployees.xsl"))
    // Note that this ProgID may change
    var xslTemplate=new ActiveXObject("MSXML2.XSLTemplate")
    // Compile the stylesheet
    xslTemplate.stylesheet=xslDoc
    var proc=xslTemplate.createProcessor()
    // Set the source of the data
    proc.input=xmlDoc
    // Set the destination of the output, here to a DIV in an HTML page
    proc.output=response
    // Transform the document
    proc.transform
    %>

    XML 和 XSL 原始文档被声明为 FreeThreadedDOMDocument 对象。 无论 XML 文档位于何处,通常都可以使用此声明,但是只有在使用新数据集时它才是必需的。 不过,请在 Microsoft 提供的最新文档中查找有关此声明或者本节中讨论的任何 progID 或接口信息的明确单词,因为它们几乎肯定会发生更改。

    为了使用编译后的 XSL,您需要创建一个显式的 XSL Template 对象。 该对象只有一个属性 (stylesheet) 和一个方法 (createProcessor)。 首先将希望编译的 XSL 对象指定给 stylesheet 属性,然后调用 createProcessor以实际编译它。 得到的对象是编译样式表或处理器。

    与 DOMDocument 对象的 transformNode 函数不同,该处理器的 transform 方法不直接输出任何文本。 相反,它假设某个 XML DOM 对象(通过 input 属性)被指定为输入对象,并在被调用到内部流对象时放置它的结果。 如果您将 output属性设置为支持流的对象(例如,ASP Response 对象),则数据将输出到所支持的流。 否则,您可以直接查询 output 属性并将它指定给一个字符串变量 — XML DOM、记录集或者支持流的任何其他接口。

    返回页首
    使用参数
    我之所以越来越依赖 XSL 是因为它提供一个功能强大的不同编程方法,它更多地基于模式而非过程。 但是,经过证明,旧的实现有一个时常令人烦恼的缺陷 — 几乎不可能以有效的方式执行参数化。 参数化在本质上是指如下能力:将信息传递到 XSL 文档,以便它可以基于一些外部信息来更改其输出。

    参数化的最大问题是 XSL 不知道它在旧实现中的情况。 如果您希望基于所面向的浏览器来更改 XSL 模板某个部分中的输出结果,例如,理论上,您可以检索或许已经由某个 ID 标识的 <xsl:if> 或 <xsl:when> 参数,修改 xsl:expr属性,然后执行查询。 换句话说,您需要过于深入地了解 XSL 文档内部工作方式,而这与封装的粒度(XML 和面向对象的编程的核心)相抵触。

    幸运的是,1999 年 11 月的 XSL 转换规范(即,W3C 针对 XSL-T 提出的明确建议)包含一个在 XSL 文档中传递参数的功能。(注: 为了处理此实现,您需要将命名空间更改为 “http://www.w3.org/TR/1999/Transform”。) 在 XSL 中,参数是通过使用 <xsl:param> 标记来指示的。 这会将参数名(包含在 “name” 中,这毫不奇怪)属性与包含在两个位置之一中的内容(要么作为 “selected” 属性,要么作为参数标记中包含的文本)相关联。 顺便说一句,这些内容不必是简单的值 — 您实际上可以通过参数来直接传入 XML。

    在 XSL 结构本身中,参数的值可以通过许多不同的方法来检索。 在 <xsl:if> 或 <xsl:when> 测试属性中,可以通过在参数名称的前面放置一个 “$” 来访问它。 下面的转换将检索收入高于 10 万美元的所有雇员:

    <!-- getSalaryThresholdEmployees.xsl -->
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:param name="salaryThreshold">100000</xsl:param>
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <xsl:template match="*|@*|text()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()"/></xsl:copy>
       </xsl:template>
    <xsl:template match="employee">
    <xsl:if test="salary[number(.)>$salaryThreshold]">
          <xsl:copy><xsl:apply-templates select="*"/></xsl:copy>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    您还可以使用参数将值直接输出到流中。 <xsl:value-of select="$paramName"> 节点可用于输出参数化值的文本,而 <xsl:copy-of select="$paramName"> 可用于输出 XML(如果参数中包含它的话)。

    此代码的功能作用于上一节中描述的处理器对象。 当您在 XSL 脚本中设置了一个参数之后、再次转换新值之前,可以使用 addParameter方法将新值传递到该参数。下面的 ASP 脚本显示如何将上述功能与 Web 页面结合使用:

    <%@script language="VBScript"%>
    <%
    dim xmlDoc
    dim xslDoc
    if session("xmlDoc")=Nothing then
    set xmlDoc=createObject("MSXML2.FreeThreadedDOMDocument")
    xmlDoc.load server.mapPath("Msdn-employees3.xml")
    set xslDoc=createObject ("MSXML2.FreeThreadedDOMDocument")
    xslDoc.load server.mapPath("getSalaryThreshholdEmployees.xsl")
    set xslTemplate=createObject("MSXML2.XSLTemplate")
    xslTemplate.stylesheet=xslDoc
    set proc=xslTemplate.createProcessor
    session("xmlDoc")=xmlDoc
    session("proc")=proc
    else
       set xmlDoc=session("xmlDoc")
       set proc=session("proc")
    end if
    ' Set the source of the data
    proc.input=xmlDoc
    ' Set the destination of the output, here to a DIV in an HTML page
    proc.output=response
    ' Transform the document
    ' Retrieve the salary from the query string, and add it as a parameter
    proc.addParameter "salaryThreshhold",request("salary")
    proc.transform
    %>

    在本例中,像处理器对象一样,包含雇员的 xmlDoc 只在会话开头加载一次。 如果会话超时,这些项会从头重新创建。 addParameter 方法随后将薪金从 IIS Request 对象传递到参数节点。 <xsl:param> 节点自动追加到文档中并且总是被首先编译。 给定名称的最后一个参数是分析器将使用的参数,这就是为什么会添加参数而不会显式更改参数(有时这可能是更为复杂的操作)。

    如果您一直使用旧分析器,则尽管参数化需要慢慢习惯,但是参数化为 XSL 添加的功能是不可低估的。 有了参数,XSL 通常就会对 ASP 开发产生很大的影响。

    返回页首
    使对象成为 XSL 友好对象
    正如前面所讨论的那样,在 XSL 中调用 ActiveX 对象会产生几个大问题。 一个最大的问题与封装有关。 XSL 文档应当是一个自我封闭的世界,即一个完整的黑盒子 — 除了预期输入(XML 数据源和参数)和输出(某种流),您应当无需知道有关如何处理 XSL 实体的更多信息。 然而,通过将 ActiveX 声明(或者任何相关代码)放到该盒子中,您将被迫作为转换用户来了解内部工作原理。

    一个相关的问题是,XSL 文档中的任何对象只知道可在脚本分析器中定义的环境。 对于某些事情(例如,IIS 中的 ASP 对象),这可能是一个实际问题,因为您不能从 XSL 中方便地使用 IIS(您也无需这样做)。

    作为其 XML 产品的最大使用者,Microsoft 已经屡次面临这种局限,而且坦白地说,它们提供的解决方案非常好。 在新分析器中,您不必在脚本中定义 ActiveX 对象,而是在 XSL 模板中声明命名空间扩展,然后使用处理器将一个对象指定到该命名空间。

    这似乎不是特别清楚,但是我将举例说明我所谈论的内容。 假设我有一个用 Visual Basic 编写的业务对象,它的 progID 为 “fabrikam.employeeUtils”,它执行三个函数:

    • 对于给定的雇员 ID,它基于当前的评分和薪金计算新薪金 (fabrikam.newSalary)。

    • 它检验评分是否低于可接受的水平 (fabrikam.isUnsatisfactory)。

    • 它指出是否需要发出警告和解雇通知书 (fabrikam.notifyUnsatisfactory),并以字符串形式返回违规级别。

    这些函数的具体实现基本上与本文无关(尽管我将略微详细地介绍如何处理它们)。

    新的 XSL 分析器允许您创建一个可用于将对象与转换关联的命名空间。 在本例中,我将该命名空间随意设置为 fabrikam=http://www.fabrikam.com/employeeUtils。 就该命名空间本身而言,它只能用来将该自己呈现为唯一,而无需显式指向任何内容。 在对此进行设置之后,您可以创建到属性或方法的引用,如同它们是 XPath 查询中的路径一样。 例如,<xsl:value-of select="fabrikam:newSalary(.)"/> 会将当前的上下文传递到 newSalary 方法,然后检索要显示在输出流中的结果。 整个转换看上去可能如下所示:

    <!-- Update Salary with Objects-->
    <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/TR/1999/Transform"
    xmlns:fabrikam="http://www.fabrikam.com/employeeUtils"
    >
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <xsl:template match="employee">
          <xsl:apply-templates/>
       </xsl:template>
       <xsl:template match="rating">
          <xsl:copy><xsl:value-of/></xsl:copy>
          <xsl:if test="fabrikam:isUnsatisfactory(.)">
             <probation>
    <xsl:value-of select="fabrikam:NotifyUnsatisfactory(.)"/>
    </probation>
          </xsl:if>
       </xsl:template>
       <xsl:template match="probation"/>
       <xsl:template match="salary">
          <xsl:copy><xsl:value-of
          select="fabrikam:newSalary(.)"/></xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    这与上一节“合并脚本”中描述的转换基本相同,主要区别在于此处的脚本是直接与外界通讯的。 在由 fabrikam 命名空间描述的所有函数中,您将看到正在传递一个句点。 此句点就是当前的上下文(任何其他上下文也可能已经包括在此处),并且在本质上具有 XML DOM NodeList的特征(为了响应 XPath 查询而生成的元素列表),该列表中通常只包含一个元素。 对于 isUnsatisfactory 函数的实现显示它将如何定义(在 Visual Basic 中):

    Function isUnsatisfactory(nodeList as IXMLDOMSelection) as String
       Dim ratingNode
    Dim rating as String
       Dim probationNode
       Set ratingNode=nodeList(0)
       Rating=ratingNode.nodeTypedvalue
       If rating<4 then
          Set probationNode=ratingNode.selectSingleNodes(_
          "//ancestor::employer/probation")
          if probationNode is nothing then
             isUnsatisfactory="Warning"
          else
             isUnsatisfactory="Terminated"
          end if
       else
          isUnsatisfactory=""
       end if
    End Function

    在该组件创建之后,您需要使用 addObject 方法将该对象传递到 XSL 转换中。 这会将该对象绑定到所提供的命名空间,这样,当您调用处理器的 transform方法时,该转换可以与外部对象通讯,如下所示:

    proc.input=xmlDoc
    ' Set the destination of the output, here to a DIV in an HTML page
    proc.output=response
    ' Transform the document
    ' Retrieve the salary from the query string, and add it as a parameter
    set axeman=createObject("fabrikam.employeeUtils")
    proc.addObject axeman, "http://www.fabrikam.com/employeeUtils"
    proc.transform

    请注意,您在此处传递的是命名空间,而非它的前缀。 作为 XSL 模板的使用者,您应当无需知道它使用什么命名空间前缀,而只需知道您可以将正确的对象传递给适当的前缀。 (如果您将命名空间视为 GUID,这会有所帮助 — 它只是一种将对象与和它相关联的前缀相匹配的方法。)

    最后一个诀窍是: 您仍可以使用 XSL 文档中的脚本,但是它们现在利用与上述机制相同的机制 — 先声明一个命名空间,然后将该命名空间的声明映射到适当的前缀。 例如,如果方法是在内部定义的,它们看上去将如下所示:

    <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/TR/1999/Transform"
    xmlns:fabrikam="http://www.fabrikam.com/employeeUtils"
    >
       <msxml:script implements-prefix="fabrikam"
    language="Vbscript">
          function IsUnsatisfactory(nodeList)
             ' Implementation for isUnsatisfactory
          end function
          function NotifyUnsatisfactory(nodeList)
             ' Implementation for NotifyUnsatisfactory
          end function
          function NewSalary(nodeList)
             ' Implementation for NewSalary
          end function
       </msxml:script>
       <xsl:template match="/">
          <xsl:apply-templates select="*"/>
       </xsl:template>
       <xsl:template match="*|@*|text()|cdata()|comment()|pi()">
          <xsl:copy><xsl:apply-templates
          select="*|@*|text()|cdata()|comment()|pi()"/></xsl:copy>
       </xsl:template>
       <xsl:template match="employee">
          <xsl:apply-templates/>
       </xsl:template>
       <xsl:template match="rating">
          <xsl:copy><xsl:value-of/></xsl:copy>
          <xsl:if test="fabrikam:IsUnsatisfactory(.)">
             <probation>
    <xsl:value-of select="fabrikam:NotifyUnsatisfactory(.)"/>
    </probation>
          </xsl:if>
       </xsl:template>
       <xsl:template match="probation"/>
       <xsl:template match="salary">
          <xsl:copy><xsl:value-of select="fabrikam:NewSalary(.)</xsl:copy>
       </xsl:template>
    </xsl:stylesheet>

    因为该脚本块不再是基本 XSL 规范的一部分,所以它现在属于 msxml 命名空间(即,应表示为 <msxml:script>,而非 <xsl:script>)。 implements-prefix 属性又通知分析器,该脚本块中的方法实现其命名空间前缀在属性值中指定的对象;即,在该脚本块中用 “fabrikam” 的 implements-prefix定义的函数NewSalary 将不同于在该脚本块中用 “ace” 的 implements-prefix 定义的 NewSalary。

    作为设计注释,对于接口组件(尤其是对于自行设计的组件),您可能希望考虑在脚本中声明接口存根。 在应用 addObject 时,较新的实现会改写较旧的存根,但是“虚拟”函数(从 Microsoft Visual C++? 借鉴的术语)将有助于代码保持一致。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    我只知道,用 xml 做网站,可以省我很多时间.

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/7/21 13:37:00
     
     qqonline 帅哥哟,离线,有人找我吗?天蝎座1983-11-9
      
      
      头衔:XML不是做网站用的
      等级:大一新生
      文章:5
      积分:83
      门派:W3CHINA.ORG
      注册:2004/7/5

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给qqonline发送一个短消息 把qqonline加入好友 查看qqonline的个人资料 搜索qqonline在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 引用回复这个贴子 回复这个贴子 查看qqonline的博客2
    发贴心情 
    闷头看了个大概
    个人认为,有些东东似乎没有必要。

    同样是以降低通用性来换取高效
    那为什么不直接用解析器中编程
    而要麻麻烦烦地在XSL中加脚本呢?

    也许我理解有问题,欢迎指正

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/7/22 14:56:00
     
     GoogleAdSense天蝎座1983-11-9
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 XSL/XSLT/XSL-FO/CSS 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2025/9/28 18:31:40

    本主题贴数2,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    171.875ms