Re: [xsl] how to store node in variable?

Subject: Re: [xsl] how to store node in variable?
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 21 Oct 2004 17:38:53 -0400
Hi John,

At 03:39 PM 10/21/2004, you wrote:
Sorry to trouble the list with this one which seems so simple and yet somehow I am banging my head against the wall for hours and I can't even think what to search for. The worst part is I think I've done this before...

How do I store a reference to a node in a variable or what am I doing wrong? Here is what I have (maybe oversimplified) :

<xsl:variable name="me">
  <xsl:choose>
    <xsl:when test="<some condition>">
      <xsl:copy-of select=".." />
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable> <xsl:call-template name="<some template>">
  <xsl:with-param name="myid" select="$me/@id" />
</xsl:call-template>

The with-param is always throwing an error, expression should result in a node-set.

You are running up against a restriction in the design of XSLT 1.0, which in keeping with its philosophy of declarative, "side-effect-free" processing, does not want to let you process the results of your own transformation. (XSLT 2.0 doesn't have this restriction.)


Accordingly, your copy-of instructions are not creating node sets, but rather a data object called a "result tree fragment", which you can copy out into your result tree, and which you can operate on as a string, but which you cannot traverse, query into, or process as a node set.

This introduces an important distinction between

<xsl:variable name="me" select="/set/node"/>

and

<xsl:variable name="me">
  <xsl:copy-of select="/set/node"/>
</xsl:variable>

In the first case, $me is an honest node set, and you can do anything with it you can do on any node set, such as apply-templates to it. But in the second case, it's a result tree fragment, and you can't.

In order to apply this in your conditional case, you need to express the conditional directly in the XPath. So:

<xsl:variable name="condition" select="boolean( ... your conditional ...)"/>
<xsl:variable name="me"
  select="self::node()[$condition] | parent::node()[not($condition)]"/>

(You could of course avoid using that $condition variable by putting the conditional expression itself into those predicates: here, I include it for clarity and to force its value to a boolean, which it may be in any case, in the event.)

If I add msxsl:node-set around my selects in the variable definition I get cannot convert result tree fragment to node-set.

In more complex cases, you can convert the RTF into a node set using a node-set() function, but you wouldn't do it in the way you suggest (on a select in a copy-of instruction, which by definition creates a *copy* of a node set and does not bind the node set itself to the variable), but rather on the variable itself:


<xsl:with-param name="myid" select="exsl:node-set($me)/@id" />

... though note that since a result-tree-fragment has a little root of its own, this may actually have to be exsl:node-set($me)/*/@id or some such.

Alternatively, is there any way to explicitly set the context node without using for-each?

Sure: you can apply templates to the node you want to be the context node. :-> But that won't solve your problem: it has to be a node before it can be a context node.


I hope this helps,
Wendell


====================================================================== Wendell Piez mailto:wapiez@xxxxxxxxxxxxxxxx Mulberry Technologies, Inc. http://www.mulberrytech.com 17 West Jefferson Street Direct Phone: 301/315-9635 Suite 207 Phone: 301/315-9631 Rockville, MD 20850 Fax: 301/315-8285 ---------------------------------------------------------------------- Mulberry Technologies: A Consultancy Specializing in SGML and XML ======================================================================

Current Thread