Java中XSLT转换XML节点值异常:科学计数法问题求助
问题背景
你的Java XSLT转换程序已经稳定运行10年,近期突然出现异常:处理源XML中的<Sold>1064623.94</Sold>节点时,Java程序输出的结果是<Sold amountCurrencyID="EUR">1.06462394E6</Sold>(科学计数法格式),但在线XSLT转换工具能输出正确的<Sold amountCurrencyID="EUR">1064623.94</Sold>。这种格式问题影响了后续Java方法的执行,你怀疑问题出在javax.xml.Transformer或xml-apis-1.3.04.jar库,当前使用的是Saxon的TransformerFactoryImpl。
相关代码片段
XSLT节点代码
<Sold> <xsl:attribute name="amountCurrencyID" namespace="">EUR</xsl:attribute> <xsl:value-of select="string(number(string($var4_resultof_first/*[local-name()='Sold' ])))"/> </Sold>
Java转换核心代码
public void convert() throws ClassNotFoundException, InstantiationException, IllegalAccessException, TransformerException { TransformerFactory transformerFactory = new net.sf.saxon.TransformerFactoryImpl(); Source xsltSource = new StreamSource(new ByteArrayInputStream(this.getUserInfo().getXslt())); Templates templates = transformerFactory.newTemplates(xsltSource); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Source xmlSource = new StreamSource(getBais()); Result result = new StreamResult(getBaos()); Transformer transformer = templates.newTransformer(); transformer.setErrorListener(new XsltErrorListener()); if(this.getParametros()!=null && this.getParametros().size()>0){ String nombre=""; for (Enumeration enumer = this.getParametros().keys();enumer.hasMoreElements();){ nombre=(String)enumer.nextElement(); transformer.setParameter(nombre, this.getParametros().get(nombre)); } } transformer.transform(xmlSource, result); }
问题根源
问题出在XSLT中多余的嵌套转换逻辑:string(number(string(...)))。
- 源节点
<Sold>的值本身已经是字符串类型的数值,你先转成字符串(冗余操作)、再转成数值、最后又转回字符串。 - 当把较大的浮点数转回字符串时,Saxon(受版本或配置影响)会采用科学计数法格式化,而在线工具使用的处理器可能默认保留标准十进制格式。
- 这层多余的数值转换完全是画蛇添足,反而引入了数值格式化的不确定性。
解决方案
方案1:移除冗余转换(最优解)
直接使用源节点的原始字符串值,彻底避免数值转换带来的格式化问题:
<Sold> <xsl:attribute name="amountCurrencyID" namespace="">EUR</xsl:attribute> <xsl:value-of select="$var4_resultof_first/*[local-name()='Sold']"/> </Sold>
这个方法既能完全保留原始格式,又减少了不必要的计算开销,是最可靠的解决方式。
方案2:用format-number()显式控制格式
如果必须确保值是合法数值后再输出,可以用format-number()强制指定非科学计数法的格式:
<Sold> <xsl:attribute name="amountCurrencyID" namespace="">EUR</xsl:attribute> <xsl:value-of select="format-number(number($var4_resultof_first/*[local-name()='Sold']), '#.00')"/> </Sold>
其中#.00表示保留两位小数,你可以根据实际需求调整格式:比如#.##表示最多保留两位小数,0.00则强制显示两位小数(即使末尾是0)。
方案3:检查Saxon版本与配置
如果近期更新过依赖库(比如Saxon或xml-apis),版本变更可能导致数值格式化行为改变:
- 对比当前使用的Saxon版本和之前稳定运行的版本,查看版本差异文档;
- 可以尝试为
Transformer设置Saxon特有的参数,控制数值输出格式(具体参数参考对应版本的官方文档)。临时解决的话,回退到之前稳定的库版本也是可行的,但长期来看优化XSLT逻辑更稳妥。
验证建议
修改XSLT后,先通过在线工具测试确认输出符合预期,再部署到Java程序中验证结果。如果是版本问题,回退库版本时要确保不会影响其他依赖功能。
内容的提问来源于stack exchange,提问作者javalderas

