Re: [xsl] How to select a node in an xpath expression ?

Subject: Re: [xsl] How to select a node in an xpath expression ?
From: Piet van Oostrum <piet@xxxxxxxxxxxxxx>
Date: Wed, 8 Dec 2010 16:55:06 -0400
Vincent De Groote wrote:

 > In the template matching <field-group> elements , I didn't choose an
 > iteration with a for-each instruction, because the for-each cannot be
 > "stopped" after the first match, and it could therefore produce
 > multiple matches.  Instead, I tried to select the correct replacement
 > in a variable:
 > <xsl:variable name='current-field-group' select='current()'/>
 > <xsl:variable name='replacement'
 > select='$replacements/replacement[field/@name =
 > $current-field-group/xxx:field-sequence/xxx:field[position()
 > &lt;=count(field)]/@name]'/>'

This expression also loops over all possible replacements and therefore produces multiple matches too. In both cases you can select the first match with the predicate [1], although I would suspect the expression to have a bigger chance to get optimized than a for-each followed by [1].
 > This replacement variable is always empty.  The expected result of
 > the replacement predicate is to match when the sequence of
 > [$replacements/replacement]/field/@name is equal to the sequence of
 > [$current-field-group/field-sequence/]field/@name, limiting the list
 > to the number of fields to be replaced.  The count(field)
 > sub-expression is supposed to count the number of field children of
 > the $replacements/replacement.
 > Is the last sub-expression "count(field)" understood as the count of
 > the $current-field-group/field-sequence field children instead of the
 > $replacements/replacement field children ?  If this is true, how can
 > i select the field children of the current replacement ?
Predicates are always evaluated in the context of the item to the left of the [, which in this case is xxx:field in this case, and obviously the count = 0. You can refer to the context item outside of the total expression with current() but AFAIK not to intermediate items (how would you specify this). So I think you still have to fall back to the for-each so that you can refer to the replacement item with current().

And by the way, your comparison wouldn't work even when count(field) could refer to the proper item. You are comparing two sequences of name attributes and this will be true if there is some value in the left one that is equal to some value in the right one, not when the two sequences are equal. For that you have to use deep-equal (fortunately you are using XSLT 2.0).

I came up with something like this. You have to adapt it to fit in your stylesheet. This is just to illustrate how the selection of the replacement can be made.

<xsl:template match="xxx:field-group">
  <xsl:variable name='current-field-group' select='current()'/>

  <xsl:variable name='replacement'>
    <xsl:for-each select="$replacements/replacement">
      <xsl:if test="deep-equal(field/@name, subsequence ($current-field-group/xxx:field-sequence/xxx:field/@name, 1, count(field)))">
	      <repl sel="{@replace-by}"/>
  <xsl:copy-of select="$replacement/repl[1]"/>

Piet van Oostrum
Cochabamba. URL:
Nu Fair Trade woonartikelen op

Current Thread