性能更高的XSLT - 输出中的选择性包含

时间:2010-02-16 20:46:36

标签: xslt

我创建了一个样式表,它应该有选择地复制XML文档的内容,以便我可以删除不需要的数据。我在下面提供了2个示例以及我们当前用来执行此操作的样式表。样式表有效,但我认为可能有更好的方法,因为在当前版本中我在两个不同的位置检查相同的东西(author ='John Doe')。

在输出中包含xml元素的规则如下:

  • 如果记事本中的记事本元素的作者文本等于'John Doe',则在输出中包含记事本元素
  • 如果记事本元素的文字等于'John Doe'的作者元素,则在xml输出中包含记事本元素中的所有元素。

输入示例#1

<transaction>  
<policy>  
    <insco>CC</insco>  
    <notepads>  
      <notepad>  
        <author>Andy</author>  
      <notepad>  
      <notepad>  
        <author>John Doe</author>  
      <notepad>  
      <notepad>  
        <author>Barney</author>  
      <notepad>  
    </notepads>  
  </policy>  
</transaction>

输入#1的预期结果

<transaction>
  <policy>
    <insco>CC</insco>
    <notepads>
      <notepad>
        <author>John Doe</author>
      <notepad>
    </notepads>
  </policy>
</transaction>

输入示例#2

<transaction>
  <policy>
    <insco>CC</insco>
    <notepads>
      <notepad>
        <author>Andy</author>
      <notepad>
    </notepads>
  </policy>
</transaction>

输入#2的预期结果

<transaction>
  <policy>
    <insco>CC</insco>
  </policy>
</transaction>

样式表的当前版本

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fn">
  <xsl:template match="*">
      <xsl:choose>
        <xsl:when test="name()='notepads'">
          <xsl:if test="/transaction/policy/insco='CC' and (notepad/author='John Doe')">
            <xsl:copy>
              <xsl:apply-templates />
            </xsl:copy>              
          </xsl:if>
        </xsl:when>
        <xsl:when test="name()='notepad'">
          <xsl:if test="author='John Doe'">
            <xsl:copy>
              <xsl:apply-templates />
            </xsl:copy>              
          </xsl:if>                
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy>
            <xsl:apply-templates />
          </xsl:copy>
        </xsl:otherwise>
      </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

2 个答案:

答案 0 :(得分:2)

使用模板,它们通常更有效,并且避免name()检查,它们是缓慢且不可靠的(前缀和名称空间不适合这些):

<?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:template match="*">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="notepads">
        <xsl:if test="(ancestor::policy/insco='CC') and (notepad/author='John Doe')">
            <xsl:copy>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <xsl:template match="notepad">
        <xsl:if test="author='John Doe'">
            <xsl:copy>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:2)

我可以想到两种方法来做到这一点。

1)身份模板,硬编码作者姓名:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

  <!-- nodepads generally get removed... -->    
  <xsl:template match="notepad" />

  <!-- ...unless their author is 'Jon Doe' -->    
  <xsl:template match="notepad[author='John Doe']">
    <xsl:copy-of select="." />
  </xsl:template>

</xsl:stylesheet>

2)修改了身份模板,XSL密钥,参数化作者姓名:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:param name="theAuthor" select="'John Doe'" />

  <xsl:key 
    name="kNotepad" match="notepad[author]" 
    use="concat(generate-id(..), '|', author)" 
  />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="
        node()[not(self::notepad)]
        |key('kNotepad', concat(generate-id(), '|', $theAuthor))
        |@*" 
      />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

第二种方法需要一点解释:

  • <xsl:key>为所有<nodepad>个节点编制索引,这些节点的父级唯一ID和作者姓名为<author>
  • 我们说<notepads>的唯一ID是'id0815',那么您感兴趣的<notepad>的关键是'id0815|Jon Doe'
  • 身份模板会复制通过它的每个节点。它的修改方式是通过它自己找到的每个节点,而只是:
    • 任何非<notepad>的节点:node()[not(self::notepad)]
    • 任何属性:@*
    • 密钥返回的任何节点。
  • key()的调用自然只会在<notepads>个元素上返回任何内容(因为它包含他们的唯一ID)
  • 因此,当模板当前正在处理<notepads>元素(在我们的情况下为'id0815')时,key()将仅返回其'Jon Doe'个子元素,在所有其他情况下,它将返回转空
  • 与解决方案1)相比,这个可以通过参数提供,改变其行为而不改变其代码
  • 值得注意的是,所有内容都按输入文档顺序排列