Why descendant-or-self:: is not allowed in templat

2019-04-16 17:45发布

Suppose I have the following XML (which is the embedding of TEI annotation scheme into 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>

Now I want to copy all annotation nodes as is into resulting XHTML but only rename <title> to <bibTitle> (as <title> is only allowed in <head>), so I used the following transformation:

<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>

However it does not compile and breaks with following error:

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

When I change the match rule to <xsl:template match="tei:bibl|tei:bibl//*"> it starts working as intended. But that should be identical to descendant-or-self::*, right? Have I hit the transformer implementation limitation here?

First I've tested with Mozilla 3.5 internal transformer, then with Xalan 2.7.1 – same negative result.

标签: xslt
4条回答
疯言疯语
2楼-- · 2019-04-16 18:20

It's a hard requirement by the spec:

Although patterns must not use the descendant-or-self axis, patterns may use the // operator as well as the / operator.

查看更多
何必那么认真
3楼-- · 2019-04-16 18:22

This limitation is valid only for any location step within the template's match pattern. It is by design (mandated by the W3C XSLT 1.0 and XSLT 2.0 specifications) -- to ensure efficient XSLT processing.

Do note: One can freely use any axis (including descending-or-self::) withinin the predicates that follow any location step.

Update:

Here is a short, complete example of using the descendant-or-self:: axis in the match attribute of xsl:template:

<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>

when this transformation is applied on the following XML document:

<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>

the wanted result: any num elements with value >= 5 are deleted:

<nums>
  <num>1</num>
  <num>2</num>
  <num>3</num>
  <num>4</num>
  <num>5</num>
</nums>
查看更多
闹够了就滚
4楼-- · 2019-04-16 18:27

Here's a way you can rewrite the pattern into an equivalent one that doesn't repeat mention of tei:bibl:

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

As to why the limitation is there, the general answer, yes, is for performance. Perhaps the limitations are overly conservative, because, as you pointed out, the rewrite of descendant-or-self in this case is trivial.

I regularly get annoyed by this limitation (that you can use // but not descendant).

查看更多
我命由我不由天
5楼-- · 2019-04-16 18:34

Here is a case where it is not enough:

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

now I want to match only:

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

i.e., if the predicate p(.) is true on a, I want a/c, a/c/c, a/c/c/c and if it is true on b, I want b/c, b/c/c, and b/c/c/c.

But I do not want a/b/c, a/b/c/c, etc. just because the predicate matches on a and not on b.

If I make a match pattern:

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

then I match all of them which I do not want.

So I have to do it backwards in the bracket:

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

I think I just convinced myself that this restriction isn't really a logical restriction, however, I think the excuse not to allow proper axis steps in match patterns is pretty lame, because when I need this, I need this, who cares if it is not as fast as if I use simpler expressions.

查看更多
登录 后发表回答