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

使用输入参数时xml-to-json number-formatter函数抛出类型转换异常的解决方法

问题

编写了如下XSLT样式表,定义test:format-json-number函数作为fn:xml-to-json的number-formatter参数。当函数引用输入参数$input时,转换会抛出ClassCastException,提示net.sf.saxon.value.SingletonClosure无法转换为net.sf.saxon.value.StringValue;但如果函数返回xsl:text字面量或引用字面量变量则运行正常。请问如何定义可引用输入参数的number-formatter函数以避免该异常?

样式表代码

<xsl:stylesheet
    version="1.0"
    xmlns:test="test"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="#all">

    <xsl:function name="test:format-json-number" as="xs:string">
        <xsl:param name="input" as="xs:string"/>
        <xsl:value-of select="$input"/>
    </xsl:function>
    
    <xsl:template match="test:message">
        <xsl:variable name="result">
            <fn:map>
                <fn:number key="studentId">
                        <xsl:text>1</xsl:text>
                </fn:number>
            </fn:map>
        </xsl:variable>
        <xsl:value-of select="fn:xml-to-json($result, map{'number-formatter': test:format-json-number#1})"/>
    </xsl:template>
</xsl:stylesheet>

异常堆栈信息

java.lang.ClassCastException: class net.sf.saxon.value.SingletonClosure cannot be cast to class net.sf.saxon.value.StringValue (net.sf.saxon.value.SingletonClosure and net.sf.saxon.value.StringValue are in unnamed module of loader 'app')
        at net.sf.saxon.ma.json.JsonReceiver.endElement(JsonReceiver.java:287)
        at net.sf.saxon.event.DocumentValidator.endElement(DocumentValidator.java:79)
        at net.sf.saxon.tree.tiny.TinyElementImpl.copy(TinyElementImpl.java:477)
        at net.sf.saxon.tree.tiny.TinyDocumentImpl.copy(TinyDocumentImpl.java:303)
        at net.sf.saxon.ma.json.XMLToJsonFn.convertToJson(XMLToJsonFn.java:118)
        at net.sf.saxon.ma.json.XMLToJsonFn.process(XMLToJsonFn.java:100)
        at net.sf.saxon.expr.SystemFunctionCall$SystemFunctionCallElaborator.lambda$elaborateForPush$8(SystemFunctionCall.java:692)
        at net.sf.saxon.expr.instruct.ValueOf$ValueOfElaborator.lambda$elaborateForPush$1(ValueOf.java:425)
        at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:376)
        at net.sf.saxon.trans.Mode.handleRuleNotNull(Mode.java:587)
        at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:521)
        at net.sf.saxon.trans.rules.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:72)
        at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:518)
        at net.sf.saxon.trans.XsltController.applyTemplates(XsltController.java:684)
        at net.sf.saxon.s9api.AbstractXsltTransformer.applyTemplatesToSource(AbstractXsltTransformer.java:431)
        at net.sf.saxon.s9api.Xslt30Transformer.applyTemplates(Xslt30Transformer.java:307)
        at net.sf.saxon.Transform.processFile(Transform.java:1405)
        at net.sf.saxon.Transform.doTransform(Transform.java:894)
        at net.sf.saxon.Transform.main(Transform.java:85)
java.lang.RuntimeException: Internal error evaluating template rule  at line 14 in module file:test.xslt
        at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:386)
        at net.sf.saxon.trans.Mode.handleRuleNotNull(Mode.java:587)
        at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:521)
        at net.sf.saxon.trans.rules.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:72)
        at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:518)
        at net.sf.saxon.trans.XsltController.applyTemplates(XsltController.java:684)
        at net.sf.saxon.s9api.AbstractXsltTransformer.applyTemplatesToSource(AbstractXsltTransformer.java:431)
        at net.sf.saxon.s9api.Xslt30Transformer.applyTemplates(Xslt30Transformer.java:307)
        at net.sf.saxon.Transform.processFile(Transform.java:1405)
        at net.sf.saxon.Transform.doTransform(Transform.java:894)
        at net.sf.saxon.Transform.main(Transform.java:85)
Caused by: java.lang.ClassCastException: class net.sf.saxon.value.SingletonClosure cannot be cast to class net.sf.saxon.value.StringValue (net.sf.saxon.value.SingletonClosure and net.sf.saxon.value.StringValue are in unnamed module of loader 'app')
        at net.sf.saxon.ma.json.JsonReceiver.endElement(JsonReceiver.java:287)
        at net.sf.saxon.event.DocumentValidator.endElement(DocumentValidator.java:79)
        at net.sf.saxon.tree.tiny.TinyElementImpl.copy(TinyElementImpl.java:477)
        at net.sf.saxon.tree.tiny.TinyDocumentImpl.copy(TinyDocumentImpl.java:303)
        at net.sf.saxon.ma.json.XMLToJsonFn.convertToJson(XMLToJsonFn.java:118)
        at net.sf.saxon.ma.json.XMLToJsonFn.process(XMLToJsonFn.java:100)
        at net.sf.saxon.expr.SystemFunctionCall$SystemFunctionCallElaborator.lambda$elaborateForPush$8(SystemFunctionCall.java:692)
        at net.sf.saxon.expr.instruct.ValueOf$ValueOfElaborator.lambda$elaborateForPush$1(ValueOf.java:425)
        at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:376)
        ... 10 more
Fatal error during transformation: java.lang.RuntimeException: Internal error evaluating template rule at line 14 in module file:test.xslt

解决方案

这个问题的根源在于Saxon处理number-formatter函数时,对参数的延迟求值导致类型转换异常。解决方法是将XSLT函数改写为纯XPath函数(使用XPath表达式实现,而非xsl:value-of这类指令),确保参数能被正确求值为字符串类型。

修改后的样式表代码

<xsl:stylesheet
    version="3.0"
    xmlns:test="test"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="#all">

    <!-- 使用纯XPath表达式实现函数,避免指令导致的延迟求值 -->
    <xsl:function name="test:format-json-number" as="xs:string">
        <xsl:param name="input" as="xs:string"/>
        <xsl:sequence select="$input"/>
    </xsl:function>
    
    <xsl:template match="test:message">
        <xsl:variable name="result">
            <fn:map>
                <fn:number key="studentId">
                        <xsl:text>1</xsl:text>
                </fn:number>
            </fn:map>
        </xsl:variable>
        <xsl:value-of select="fn:xml-to-json($result, map{'number-formatter': test:format-json-number#1})"/>
    </xsl:template>
</xsl:stylesheet>

关键修改点

  • 将原函数中的<xsl:value-of select="$input"/>替换为<xsl:sequence select="$input"/>:xsl:sequence会直接返回参数的实际值,而非创建延迟求值的闭包,确保Saxon能正确识别参数类型。
  • 将样式表版本从1.0升级到3.0:fn:xml-to-json和number-formatter参数是XSLT 3.0的特性,原代码中使用1.0版本属于不规范用法,升级后能更好地兼容Saxon的函数处理逻辑。

复杂逻辑示例

如果需要在格式化函数中添加数字格式化、字符串替换等复杂逻辑,同样要使用XPath表达式实现,避免使用xsl:value-of、xsl:choose等指令。例如,实现保留两位小数的格式化:

<xsl:function name="test:format-json-number" as="xs:string">
    <xsl:param name="input" as="xs:string"/>
    <xsl:sequence select="format-number(xs:decimal($input), '0.00')"/>
</xsl:function>

内容的提问来源于Stack Exchange,提问作者Apion

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:50:06