You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT3.0流处理(Saxon):用saxon:while处理多XML文件触发空指针异常

问题描述

文件夹中有多个XML文件,运行时向XSLT传入文件夹路径、文件名前缀及文件总数。使用saxon:while循环批量处理文件时触发空指针异常,但移除循环处理单个文件时代码可正常运行。使用的是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文件

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>

错误信息

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)

问题分析与解决

核心原因

代码启用了流处理模式(<xsl:mode streamable="yes"/>),但流处理与saxon:while循环、可赋值变量(saxon:assignable="yes")的组合存在兼容性问题。Saxon的流处理引擎在分析循环内动态加载文档的路径表达式时,无法正确推导节点类型,导致空指针异常。

修复方案

方案1:禁用流处理模式

若文件体积不大无需流处理,直接移除流处理声明:

<!-- 移除该行 -->
<xsl:mode streamable="yes" on-no-match="shallow-skip" />

替换为普通模式:

<xsl:mode on-no-match="shallow-skip" />

方案2:改用XSLT 3.0原生循环替代saxon:while

放弃Saxon扩展的循环和可赋值变量,使用for-each遍历文件序号,符合函数式编程范式:

<xsl:template match="Input">
  <xsl:for-each select="1 to $numberOfFiles">
    <xsl:variable name="currentCount" select=".-1"/> <!-- 从0开始计数 -->
    <xsl:variable name="fullFileName" select="concat($inputPath,$filename,'_',$currentCount,'.xml')"/>
    <out><xsl:value-of select="$fullFileName"/></out>
    <xsl:apply-templates select="doc($fullFileName)/Input/InData/InfoArray/copy-of(.)" mode="s" />
  </xsl:for-each>
</xsl:template>

同时删除可赋值变量的声明:

<!-- 移除这两个变量 -->
<!-- <xsl:variable name="fileCount" select="0" saxon:assignable="yes"/> -->
<!-- <xsl:variable name="fullFileName" select="''" saxon:assignable="yes"/> -->

若仍需保留流处理,可给doc()添加流处理参数:doc($fullFileName, map{'streamable': true()})。

方案3:封装文件处理逻辑到命名模板

将单个文件的处理逻辑隔离为命名模板,降低流处理引擎的分析复杂度:

<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:call-template name="process-file">
      <xsl:with-param name="file" select="$fullFileName"/>
    </xsl:call-template>
    <saxon:assign name="fileCount" select="$fileCount+1"/>
  </saxon:while>
</xsl:template>

<xsl:template name="process-file">
  <xsl:param name="file"/>
  <xsl:apply-templates select="doc($file)/Input/InData/InfoArray/copy-of(.)" mode="s" />
</xsl:template>

内容的提问来源于stack exchange,提问作者Chandra_S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:10:09