通过复制和访问祖先来预先计算节点集 (XSLT 1.0)

precalculating node-set via copy-of and accessing ancestors (XSLT 1.0)

我想预先计算源 XML 中节点的子树,并分别处理它们(因为我希望以不同的方式处理子集),并访问祖先的一些值。

简单的例子

<numbers count="5">
  <number value="1"/>
  <number value="2"/>
  <number value="3"/>
  <number value="4"/>
  <number value="5"/>
</numbers>

假设我有一个 xslt (MSXML) 以某种方式提取偶数节点

<?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="/">
    <evens>
      <xsl:for-each select="numbers/number">
        <xsl:choose>
          <xsl:when test="@value mod 2 = 0">
            <even>
              <xsl:attribute name="count">
                <xsl:value-of select="../@count"/>
              </xsl:attribute>
              <xsl:attribute name="value">
                <xsl:value-of select="@value"/>
              </xsl:attribute>
            </even>
          </xsl:when>
        </xsl:choose>
      </xsl:for-each>
    </evens>
  </xsl:template>
</xsl:stylesheet>

我们得到..

<evens>
  <even count="5" value="2" />
  <even count="5" value="4" />
</evens>

不错...

但是我怎样才能将过滤与处理分开,比如...

<?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 name="calculateNodes">
    <xsl:for-each select="numbers/number">
      <xsl:choose>
        <xsl:when test="@value mod 2 = 0">
          <xsl:copy-of select="."/>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="/">
    <xsl:variable name="nodes">
      <xsl:call-template name="calculateNodes"/>
    </xsl:variable>
    <evens>
      <xsl:for-each select="msxsl:node-set($nodes)/number">
        <even>
          <xsl:attribute name="count">
            <xsl:value-of select="../@count"/>
          </xsl:attribute>
          <xsl:attribute name="value">
            <xsl:value-of select="@value"/>
          </xsl:attribute>
        </even>
      </xsl:for-each>
    </evens>
  </xsl:template>
</xsl:stylesheet>

这给出了。

<evens>
  <even count="" value="2" />
  <even count="" value="4" />
</evens>

所以...祖先没有被复制。

有没有惯用的方法来摆脱这个?

复制的节点独立存在于原始树之外。在您的示例中,number 的父级是 $nodes 变量,它没有任何属性。

你为什么不简单地做:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/numbers">
    <xsl:variable name="nodes" select="number[@value mod 2 = 1]"/>
    <evens>
        <xsl:for-each select="$nodes">
            <even count="{../@count}" value="{@value}"/>
      </xsl:for-each>
    </evens>
</xsl:template>

</xsl:stylesheet>

这样你就有了一个包含对原始节点的 reference 的变量,而不是 copy。然后您还可以访问原始父级。并且变量的内容是一个节点集;你不需要转换它。

这似乎有效

<?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 name="calculateNodes">
    <xsl:for-each select="numbers/number">
      <xsl:choose>
        <xsl:when test="@value mod 2 = 0">
          <numberWrapper>
            <xsl:attribute name="count">
              <xsl:value-of select="../@count"/>            
            </xsl:attribute>
            <xsl:copy-of select="."/>
          </numberWrapper>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="/">
    <xsl:variable name="nodes">
      <xsl:call-template name="calculateNodes"/>
    </xsl:variable>
    <evens>
      <xsl:for-each select="msxsl:node-set($nodes)/numberWrapper">
        <even>
          <xsl:attribute name="count">
            <xsl:value-of select="@count"/>
          </xsl:attribute>
          <xsl:attribute name="value">
            <xsl:value-of select="number/@value"/>
          </xsl:attribute>
        </even>
      </xsl:for-each>
    </evens>
  </xsl:template>
</xsl:stylesheet>