xsltsaxon

xml-to-json number-formatter error when using input paran


I have the following stylesheet

<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>

When executing this stylesheet I get the following stack trace:

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

It seems that however this XSLT-defined number-formatter function is being called is not actually evaluating the xsl:value-of call, it is leaving it as a lazy-evaluated SingletonClosure, and instead of evaluating the expression, it simply tries to cast, and fails. This seems to happen when anything in the test:format-json-number function references the input param. Returning a xsl:text literal works fine, and defining a new variable, assigning a literal, and returning a xsl:value-of of the new variable works fine as well.

Is there a way to define a number-formatter function entirely in XSLT that references the input parameter that doesn't result in an exception?


Solution

  • I would try

    <xsl:function name="test:format-json-number" as="xs:string">
        <xsl:param name="input" as="xs:string"/>
        <xsl:sequence select="$input"/>
    </xsl:function>