为什么在模板模式中不允许使用descendant-or-self::



假设我有以下XML(这是将TEI注释方案嵌入到HTML中):

<p>(See, for example, <bibl type="journal" xmlns="http://www.tei-c.org/ns/1.0"><author>Greger IH, et al.</author> <date>2007</date>, <title>Trends Neurosci.</title> <biblScope type="vol">30</biblScope> (<biblScope type="issue">8</biblScope>): <biblScope type="pp">407-16</biblScope></bibl>).</p>

现在我想将所有注释节点复制为到生成的XHTML中,但只将<title>重命名为<bibTitle>(因为<head>只允许<title>),因此我使用了以下转换:

<xsl:template match="tei:bibl/descendant-or-self::*">
    <xsl:variable name="nodeName">
        <xsl:choose>
            <xsl:when test="name() = 'title'">bibTitle</xsl:when>
            <xsl:otherwise><xsl:value-of select="name()" /></xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <!-- Changing of the namespace occurs here, but we don't care -->
    <xsl:element name="{$nodeName}">
        <xsl:copy-of select="@*" />
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>
<xsl:template match="p/text()|tei:bibl//text()">
    <xsl:copy-of select="." />
</xsl:template>

但是它不能编译,并出现以下错误:

Only child:: and attribute:: axes are allowed in match patterns! Offending axes = descendant-or-self

当我将匹配规则更改为<xsl:template match="tei:bibl|tei:bibl//*">时,它开始按预期工作。但它应该和descendant-or-self::*是一样的,对吧?我是否已经达到了转换器实现的限制?

我先测试了Mozilla 3.5的内部转换器,然后是Xalan 2.7.1 -同样的阴性结果

此限制仅对模板匹配模式中的任何位置步骤有效。这是通过设计(由W3C XSLT 1.0XSLT 2.0规范强制要求)来确保高效的XSLT处理。

请注意:可以在任何位置步骤后面的谓词中自由使用任何轴(包括descending-or-self::)。

:

下面是在xsl:templatematch属性中使用descendant-or-self::轴的一个简短而完整的例子:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>
 <xsl:template match="num[descendant-or-self::num > 5]"/>
</xsl:stylesheet>

当此转换应用于以下XML文档:

<nums>
 <num>1</num>
 <num>2</num>
 <num>3</num>
 <num>4</num>
 <num>5</num>
 <num>6</num>
 <num>7</num>
 <num>8</num>
 <num>9</num>
 <num>10</num>
</nums>

想要的结果:删除所有value>= 5的num元素:

<nums>
  <num>1</num>
  <num>2</num>
  <num>3</num>
  <num>4</num>
  <num>5</num>
</nums>

这是规范的硬性要求:

虽然模式不能使用后代或自轴,但模式可以使用//操作符和/操作符。

这里有一种方法可以将模式重写为不重复提及tei:bibl的等效模式:

<xsl:template match="*[ancestor-or-self::tei:bibl]">

至于为什么会有限制,一般的答案是肯定的,是为了性能。也许这些限制过于保守,因为,正如您所指出的,在这种情况下,重写descendant-or-self是微不足道的。

我经常被这个限制(你可以使用//但不能使用后代)所困扰。

这是一个不够的例子:

<a>
  <b>
    <c>
      <c>
        <c/>
      </c>
    </c>
  </b>
  <c>
    <c>
      <c/>
    </c>
  </c>
</a>

现在我只想匹配:

*[self::a or self::b][p(.)]/c/descendent-or-self::c

。如果谓词p(.)对a为真,我想要a/c, a/c/c, a/c/c,如果对b为真,我想要b/c, b/c/c, b/c/c, b/c/c/c。

但我不希望a/b/c, a/b/c/c等,只是因为谓词匹配a而不匹配b。

如果我创建一个匹配模式:

*[self::a or self::b][p(.)]//c 

那么我匹配所有我不想要的。

所以我要在括号里倒着写:

c[ancestor-or-self::c/parent::*[self::a or self::b][p(.)]]

我想我只是说服了自己,这个限制并不是一个真正的逻辑限制,然而,我认为不允许匹配模式中适当的轴步的借口是非常蹩脚的,因为当我需要这个时,我需要这个,谁在乎它是否像我使用更简单的表达式一样快。

最新更新