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