flyx
flyx

Reputation: 39738

XSLT: XPath context and document()

I have an XSLT like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                         xmlns:xalan="http://xml.apache.org/xalan">

  <xsl:variable name="fooDocument" select="document('fooDocument.xml')"/>

  <xsl:template match="/">
    <xsl:apply-templates select="$fooDocument//*"/>
  </xsl:template>

  <xsl:template match="nodeInFooDocument">
    <xsl:variable name="valueFromSource" select="//someSourceElement"/>
  </xsl:template>
</xsl:transform>

In the second template, which matches nodes in the fooDocument.xml which is loaded with document(), I want to access nodes in the XML source the transformation is executed upon. This does not work with //someSourceElement, because apparently, XPath executes this path in the context of fooDocument.

A first workaround that comes to mind is this:

...
<!-- global variable -->
<xsl:variable name="root" select="/"/>

...
<!-- in the template -->
<xsl:variable name="valueFromSource" select="$root//someSourceElement"/>

...

But I cannot use this workaround, because actually, my variable is selected like this:

<xsl:variable name="valueFromSource" select="xalan:evaluate($someXPathString)"/>

$someXPathString is not crafted in the XSLT file, but loaded from fooDocument (and contains an absolute path like the one used above). Still, I need to somehow change the XPath context back to the XML source. A very hacky workaround I found is this:

<xsl:for-each select="$root[1]">
  <xsl:variable name="valueFromSource" select="xalan:evaluate($someXPathString)"/>
</xsl:for-each>

The (useless) for-each loop changes the context back to the main XML source, thus the XPath evaluates correctly. But obviously, this is not an acceptable solution.

Is there a way to do this right, or can someone suggest a better workaround?

Upvotes: 3

Views: 3917

Answers (2)

Michael Kay
Michael Kay

Reputation: 163458

Have you considered doing all the computation that constructs $someXPathString using a series of global variables?

<xsl:variable name="fooDocument" select="document('fooDocument.xml')"/>

<xsl:variable name="temp1"
  .. some computation using fooDocument ..
</xsl:variable>

<xsl:variable name="temp2"
  .. some computation using temp1 ..
</xsl:variable>

<xsl:variable name="someXPathString"
  .. some computation using temp2 ..
</xsl:variable>

<xsl:variable name="root" select="xalan:evaluate($someXPathString)"/>

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167691

Even if you think your attempt with a for-each select="$root" to change the context document is not acceptable this is the right approach. So use that, there is no other way.

Upvotes: 3

Related Questions