XSLT:在键函数中使用变量

时间:2012-01-27 13:49:09

标签: xslt

我有以下XML文件:

<titles>
   <book title="XML Today" author="David Perry"/>
   <book title="XML and Microsoft" author="David Perry"/>
   <book title="XML Productivity" author="Jim Kim"/>
   <book title="XSLT 1.0" author="Albert Jones"/>
   <book title="XSLT 2.0" author="Albert Jones"/>
   <book title="XSLT Manual" author="Jane Doe"/>
</titles>

我想删除一些元素并应用以下XSLT:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="author1-search" match="book[starts-with(@author, 'David')]" use="@title"/>
  <xsl:template match="book [key('author1-search', @title)]" />

  <xsl:key name="author2-search" match="book[starts-with(@author, 'Jim')]" use="@title"/>
  <xsl:template match="book [key('author2-search', @title)]" />

  <xsl:template match="/">  
    <xsl:apply-templates />
  </xsl:template>

</xsl:stylesheet>

是否可以使用内联xsl变量

  <xsl:variable name="Author"> 
    <name>David</name> 
    <name>Jim</name> 
  </xsl:variable> 

而不是“author1-search”,“author2-search”等,以循环显示名称?

我只能使用XSLT 1.0(目前不支持2.0)。

提前致谢,

利奥

3 个答案:

答案 0 :(得分:6)

不,模式(在XSLT 1.0中)不能包含变量/参数引用

执行此类任务的一种方法就是这样

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:param name="pAuthor" select="'David Perry'"/>

    <xsl:key name="kBookaByAuthor" match="book"
             use="@author"/>

    <xsl:template match="/">

     Books written by <xsl:value-of select="$pAuthor"/> :<xsl:text/>
        <xsl:apply-templates
             select="key('kBookaByAuthor', $pAuthor)"/>
    </xsl:template>

    <xsl:template match="book">
     <!-- One can do more formatting here -->
     <xsl:text>&#xA;     </xsl:text>
     <xsl:value-of select="@title"/>
    </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时

<titles>
    <book title="XML Today" author="David Perry"/>
    <book title="XML and Microsoft" author="David Perry"/>
    <book title="XML Productivity" author="Jim Kim"/>
    <book title="XSLT 1.0" author="Albert Jones"/>
    <book title="XSLT 2.0" author="Albert Jones"/>
    <book title="XSLT Manual" author="Jane Doe"/>
</titles>

产生了想要的正确结果

Books written by David Perry :
  XML Today
  XML and Microsoft

更新:在评论中,OP已澄清:

  

我认为我在最初的问题中完全明确了我的要求。   正如我在我的问题和我的第一个评论中所提到的那样   有助于我看到处理多个方法的方法   作者

以下是真正使用密钥的解决方案(请注意@ Flynn1179的答案中的“密钥”不构建任何索引,只是一个常量序列字符串 - 所以使用key()的函数xsl:key实际上是在字符串列表中找到一个字符串 - 与O(N)相比,通常是O(1)用于搜索一个真正的指数):

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common">

     <xsl:output method="text"/>

     <xsl:param name="pAuthors">
      <x>David Perry</x>
      <x>Jane Doe</x>
     </xsl:param>

     <xsl:variable name="vParams" select=
      "ext:node-set($pAuthors)/*"/>

     <xsl:key name="kBookByAuthor" match="book"
                     use="@author"/>

     <xsl:template match="/">
       Books written by : <xsl:text/>

       <xsl:apply-templates select="$vParams"/>

       <xsl:apply-templates select=
        "key('kBookByAuthor', $vParams)"/>
     </xsl:template>

     <xsl:template match="book">
       <!-- One can do more formatting here -->
       <xsl:text>&#xA;     </xsl:text>
       <xsl:value-of select="concat('&quot;', @title, '&quot;')"/>
     </xsl:template>

     <xsl:template match="x">
      <xsl:if test="not(position() = 1)">, </xsl:if>
      <xsl:value-of select="."/>
     </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档(上图)时,会生成所需的正确结果

   Books written by : David Perry, Jane Doe
     "XML Today"
     "XML and Microsoft"
     "XSLT Manual"

请注意:在此解决方案中,使用Exslt函数node-set()这只是为了方便起见。在实际使用中,参数的值将在外部指定,然后不需要ext:node-set()函数。

效率:此解决方案使用XSLT中键的真正功能。使用MSXML(3,4和6)XSLT处理器进行的实验表明,如果我们搜索10000位作者,使用不同XSLT处理器的转换时间范围为:32ms到45ms。

<强>有趣的是,通过@ Flynn1179提出的解决方案不确实使密钥索引,并与许多XSLT处理器需要(对于作者的相同数量(10000))从1044ms到5564ms

MSXML3 :5564毫秒,

MSXML4 :2526ms,

MSXML6 :4867毫秒,

AltovaXML :1044ms。

对于使用真正的键索引(32ms到45ms)获得的性能,非常低劣

答案 1 :(得分:2)

XSLT 1.0中的模式不允许包含变量或参数引用,因此您不能在这些键定义或模板匹配属性中使用变量或参数引用。

答案 2 :(得分:2)

您可以在XSLT表单中的自己的命名空间中包含一个元素,而不是使用变量,并参考它,如下所示:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">
  <xsl:key name="authors" use="document('')/*/my:authors/my:name" match="/" />

  <my:authors> 
    <my:name>David Perry</my:name>
    <my:name>Jim Kim</my:name> 
  </my:authors>

  <xsl:template match="book[not(key('authors',@author))]" />

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

book模板与其作者没有相应my:name元素的模板匹配,并且不输出任何内容。身份模板输出其他所有内容,包括您关心的书籍元素。密钥有点像黑客,它基本上匹配名称所在的整个文档,而不是匹配匹配的my:name元素。既然你只关心它的存在,这应该不是问题。

或者,如果您希望传入作者列表,则可以使用:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="authors" select="'David Perry,Jim Kim'" />

  <xsl:template match="book">
    <xsl:if test="contains(concat(',',$authors,','),concat(',',@author,','))">
      <xsl:call-template name="identity" />
    </xsl:if>
  </xsl:template>

  <xsl:template match="@* | node()" name="identity">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

<xsl:if>中使用的变量而非模板中的变量匹配,但它执行相同的工作。这个特殊的代码需要指定为逗号分隔列表的作者列表,但是如果您更愿意使用不同的格式,它应该很容易适应它。