Home > Code, In The Trenches > Call external functions in any XSLT context

Call external functions in any XSLT context

September 4th, 2009 Eric Leave a comment Go to comments

Recently, I ran into a problem in an XSLT script that was preventing me from writing a file from within a template processing an attribute node. I am writing a script that takes a single XML file that references other XML files with relative paths as input (a DITA map file to be exact) and crawls the “file reference tree”, processing all files along the way. Since the majority of the files are XML this is a classic single input multiple output XSLT problem….for the most part. Some of the referenced files are graphics such as PNGs, JPGs, and GIFs. XSLT and XPath do not provide any capability to copy these file types so I chose to write an external Java class and call it from the XSLT script. XSLT purists might even say I shouldn’t be doing such a thing but I’m trying to accomplish this whole process with a single script to avoid having to track things in any intermediate XML. The simple Java class has one static method copyFile() that takes two arguments for the source location and destination location.

<xsl:stylesheet version="2.0"  ...
                         xmlns:cf="com.mypackage.CopyFile">
 
...
 
    <xsl:template match="@someattribute">
        <xsl:value-of select="cf:copyFile(string($src),string($dst))"/>
    </xsl:template>
 
...
 
</xsl:stylesheet>

The main issue with what I was doing centered around the fact that the template where I needed to perform the file write was in the XSLT context of an attribute. Saxon complained with the following error:

XTDE0410: An attribute node (audience) cannot be created after the children of the
containing element

I quickly realized that because I was using to run the custom function that I was doing something illegal in terms of what I was appending to the output tree. So I got tricky and wrapped the copyFile() call in an tag. To my surprise this actually worked. Since does not affect the output tree it allows you to do pretty much anything and continue the XSLT thread.

<xsl:message>
<xsl:value-of select="cf:copyFile(string($src),string($dst))"/>
</xsl:message>
Categories: Code, In The Trenches Tags:
  1. No comments yet.
  1. No trackbacks yet.