tubwreck
tubwreck

Reputation: 81

Using additional XML tree within XSLT stylesheet -- but document() function is not available

My XSLT stylesheet needs to load an external XML resource for use during the transformation. The external XML is essentially a lookup table needed during the transformation.

This works fine in a normal xsltproc context. I use the xslt document() function to load the XML from a URI:

<xsl:variable name="scProject" select="document('http://somepath/vx_all.xml')" />

Once the XML is available, I can do my lookup like this:

<xsl:attribute name="src">
    <xsl:for-each select="$scProject/shfb:Project/shfb:ItemGroup/shfb:Image">
        <xsl:if test="$myID=./shfb:ImageId" >
            <xsl:value-of select="substring-after(@Include,'Media\')" />
        </xsl:if>
    </xsl:for-each>
</xsl:attribute>

Unfortunately, I need to execute this XSL transformation in a context in which it is not possible to load the external XML from a URI. Instead, I need to create an <xsl:variable> that contains the XML and that can be used in the same way (e.g. that XSLT will treat as a node not as text.)

tl;dr

The particular context is a Webview VS Code extension. The XSL stylesheet is precompiled for use with the Saxon.js implementation, and the argument to document() is resolved at stylesheet compilation time, not at Webview runtime. My plan is to read the contents of the external XML resource in my extension's TypeScript code and pass it to the XSLT as a parameter, but I can't figure out how to get XSLT to treat this external parameter values as a node instead of xs:text.

Upvotes: 0

Views: 107

Answers (3)

tubwreck
tubwreck

Reputation: 81

@MichaelKay Thank you so much for your answer. In an attempt to make my question generic, I had altered some of the particulars.

The actual URI is a local path that is made up of the VSCode workspace root directory and a fixed filename. The VSCode workspace root directory is not known until the user of the extension opens a directory. The value is passed into the XSL processor as a parameter by the extension's Typescript code, and I use this xsl:variable element to load the tree:

<xsl:variable name="tdata"><xsl:value-of select="document('{$wsRoot}/vx_all.shfbproj')"/></xsl:variable>

When I used the compiled stylesheet to process my XML document from the extension, it attempts to load the resource from the compile-time workspace directory, not the run-time workspace directory. I decided to implement a workaround that involved loading the XML in my VSCode extension's Typescript code and then passing the XML itself as a parameter to the stylesheet. This is why I was looking for parse-xml().

After I read your comment, I went back and tried recompiling the stylesheet with -relocate:on, but it didn't change the behavior. After some more experimentation, I determined that I could get the desired behavior by constructing the path outside of the document() function:

<xsl:variable name="dpath"><xsl:value-of select="$wsRoot"/>/vx_all.shfbproj</xsl:variable>
<xsl:variable name="tdata" select="document($dpath)"/>

I interpret this to mean that the value of a string literal (in this case the argument to document()) is expanded at spreadsheet compilation time.

Again, thank you for your answer, and for your work on XSL. Your XSLT Programmer's Reference has been my constant companion for twenty years.

Upvotes: 0

Michael Kay
Michael Kay

Reputation: 163322

When you say: and the argument to document() is resolved at stylesheet compilation time, not at Webview runtime, I'm not entirely sure what you mean by resolved.

I suspect what you mean is that the relative URI passed to the document() function is resolved against the compile-time location of the source stylesheet, not the run-time location of the compiled stylesheet (the SEF file). There's an option -relocate:on to change this.

You also have the option of resolving the URI manually with a call on resolve-uri(), and passing the resulting absolute URI to the document() (or doc()) function.

However, I may have got the wrong end of the stick, in that your example of code that works shows document() being called with an absolute URI, which would not need to be resolved. (It's unfortunate that the word resolved is used both to mean making a relative URI absolute, and fetching the resource associated with the URI)/

Upvotes: 1

tubwreck
tubwreck

Reputation: 81

parse-xml()

That's embarrassing...

Upvotes: 1

Related Questions