Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
556 views
in Technique[技术] by (71.8m points)

xslt - Apply a predicate to the current node

(I'm posting this self-answered question because the typically offered solution to this issue is needlessly verbose and I'd like to set the record straight. I couldn't find an existing SO question for this, but if there is one, please close this as a duplicate.)

I am looking for a way to perform an XPath selection to select the current node only if it matches a certain condition. This would be useful, for example, when I want to conditionally apply an XSLT template to the current node:

<xsl:template match="Device">
  <div>
    <h2><xsl:value-of select="Name" /></h2>
    <xsl:apply-templates select="???[Featured = 'true']" mode="featured" />
    <p><xsl:value-of select="Description" /></p>
  </div>
</xsl:template>

<xsl:template match="Book">
  <div>
    <h2><xsl:value-of select="Title" /></h2>
    <xsl:apply-templates select="???[FeaturedBook = 'true']" mode="featured" />
    <h3><xsl:value-of select="Author" /></h3>
    <p><xsl:value-of select="Summary" /></p>
  </div>
</xsl:template>

<xsl:template match="node()" mode="featured">
  <p class='featured-notice'>This is a featured item!
    <a href="/AddToCart?productId={Id}">Buy now</a> to get a 15% discount.
  </p>
</xsl:template>

I have tried using .[Featured = 'true'], but I get a syntax error. How can I do this?

I'm not going to add an input and output here since they are tangential to the question and would make it exceedingly long, but if you want to see what I have in mind, I have placed them here: input, output.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The syntax .[predicate] is not allowed in XPath 1.0 on account of the syntax rules (see the end of this post for the gritty details).

100% of the advice I have found says that the only option is to use self::node() for this:

self::node()[Featured = 'true']

This XPath tester is even specifically designed to tell users to use self::node()[predicate] if they try to use .[predicate], but this is not the only option.

A valid and more concise option is to just wrap the abbreviated step in parentheses:

(.)[Featured = 'true']

This is perfectly valid by XPath 1.0 syntax rules (and in my opinion, a lot clearer).

You can also use this approach with the .. abbreviated step, even going up multiple levels:

Select grandfather node if it is featured


../..[Featured = 'true']                - Not valid

../../../*[Featured = 'true']           - Valid, but not accurate

../../self::node()[Featured = 'true']   - Valid, but verbose

(../..)[Featured = 'true']              - Valid


Addendum: Why it's not possible to use .[predicate] in XPath 1.0

The following is the definition of a "step" in XPath 1.0 (basically, the pieces of an XPath node selection expression separated by slashes are called "steps"):

[4] Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep

This means that one step consists of one of two possible options:

  • An axis specifier (which can be an empty string), followed by a node test, followed by 0 or more predicates
  • An abbreviated step: . or ..

There is no option to have an abbreviated step followed by predicates.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...