Search code examples
saxonxslt-3.0

XSLT 3.0 Streaming (Saxon) : processing multiple xml files in a folder using saxon:while is throwing error


I have multiple xml files in a folder, the number of files in the folder is going to be passed to xslt during runtime along with filename and path, when I am trying to process each file using saxon:while i am getting null pointer exception, I tried without while loop with single file and code worked.

I am sharing simplified version of xml and xslt to show issue iam facing. Iam using Saxon 11.2 EE

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://www.test.com/function" version="3.0" exclude-result-prefixes="#all" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon" >
<xsl:output method="text" omit-xml-declaration="no" indent="yes" />
<xsl:mode streamable="yes" on-no-match="shallow-skip" />
<xsl:param name="inputPath"/>
<xsl:param name="filename"/>
<xsl:param name="numberOfFiles"/>
<xsl:variable name="fileCount" select="0" saxon:assignable="yes"/>
<xsl:variable name="fullFileName" select="''" saxon:assignable="yes"/>

<xsl:template match="Input">
<saxon:while test="$numberOfFiles &gt; $fileCount">
<saxon:assign  name="fullFileName" select="concat($inputPath,$filename,'_',$fileCount,'.xml')"/>
<out><xsl:value-of select="$fullFileName"/></out>
<xsl:apply-templates select="doc($fullFileName)/Input/InData/InfoArray/copy-of(.)" mode="s" />
<saxon:assign name="fileCount" select="$fileCount+1"/>
</saxon:while>
</xsl:template>

<xsl:template  match="Info/UnitSellPrice" mode="s">
<price><xsl:value-of select="."/></price>
</xsl:template>

</xsl:stylesheet>

XML 1:

<?xml version="1.0" encoding="UTF-8"?>
<Input>
<InData>
<InfoArray>
<Info>
<UnitSellPrice>100</UnitSellPrice>
</Info>
<Info>
<UnitSellPrice>324.19</UnitSellPrice>
</InfoArray>
</InData>
</Input>

XML 2:

<?xml version="1.0" encoding="UTF-8"?>
<Input>
<InData>
<InfoArray>
<Info>
<UnitSellPrice>500</UnitSellPrice>
</Info>
<Info>
<UnitSellPrice>200</UnitSellPrice>
</InfoArray>
</InData>
</Input>

error:

Looking for function Q{http://www.w3.org/2005/xpath-functions}concat#5
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Looking for function Q{http://www.w3.org/2005/xpath-functions}doc#1
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Looking for function Q{http://www.w3.org/2005/xpath-functions}copy-of#1
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Looking for function Q{http://www.w3.org/2005/xpath-functions}concat#5
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Looking for function Q{http://www.w3.org/2005/xpath-functions}doc#1
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Looking for function Q{http://www.w3.org/2005/xpath-functions}copy-of#1
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor2720.invoke(Unknown Source)

Caused by: java.lang.NullPointerException
com.saxonica.ee.stream.Streamability.getItemType(Streamability.java:267)

Solution

  • Firstly, I would strongly encourage you to avoid using saxon:assign and saxon:while. These extensions were added to the product very early in its life and they really aren't needed now that XSLT has constructs like tunnel parameters, xsl:for-each-group and xsl:iterate. You could for example write:

    <xsl:template match="Input">
      <xsl:for-each select="1 to $fileCount">
         <xsl:variable name="fullFileName"
             select="concat($inputPath,$filename,'_',.,'.xml')"/>
         <out>{$fullFileName}</out>
         <xsl:apply-templates
             select="doc($fullFileName)/Input/InData/InfoArray/copy-of(.)" 
             mode="s" />
      </xsl:for-each>
    </xsl:template>
    

    I don't actually know whether this has any bearing on the problem, but I'm pretty sure that no-one has tested whether these extensions work in a streamable stylesheet - from the limited stacktrace supplied, it seems to have failed during the streamability analysis, and this might well mean that the streamability analysis for saxon:assign and saxon:while is defective. If that's the case and we fixed it, we would almost certainly decide that the instruction isn't streamable.

    Looking at your code more carefully, it's not clear how you are trying to use streaming. You've got a template rule that matches Input elements in streaming mode, but it doesn't actually seem to make use of any data within the Input element; while the secondary input files (accessed using doc()) are processed without streaming.