Saxon 10.6 HE中XSLT转换XML时NotSerializableException异常排查及无Serializer实现方案咨询
解决Saxon XSLT转换中的WriteAbortedException问题及替代Serializer的方法
我来帮你分析并解决这个问题,同时给出不使用Serializer的转换方案。
错误原因分析
你遇到的java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: net.sf.saxon.om.StructuredQName异常,核心原因是Saxon内部的StructuredQName类未实现Serializable接口,但在转换过程中该对象被触发了序列化操作。结合你的代码来看,可能的触发场景有两个:
- 无效的输入XML:你的输入XML存在语法错误——它包含了
<create>和<update>两个根节点,而XML规范要求文档必须有且仅有一个根元素。这种无效输入可能导致Saxon在解析和处理过程中产生异常,进而触发内部对象的序列化尝试。 - XSLT分组逻辑中的潜在问题:你的
my:merge函数使用group-by="path()"分组,虽然fn:path()返回的是字符串,但Saxon在处理分组键时可能内部使用了StructuredQName来表示节点名称,在递归处理节点的场景下导致该对象被暴露到需要序列化的上下文。
解决方案
步骤1:修复输入XML的有效性
首先将输入XML包裹在一个根元素中,修正语法错误:
<root> <create> <article> <identifier>Test</identifier> </article> <article> <identifier>Test123</identifier> </article> </create> <update> <article> <identifier>Test</identifier> </article> </update> </root>
步骤2:调整XSLT分组逻辑(可选)
如果修复XML后仍出现异常,可以修改my:merge函数的分组方式,改用自定义路径构造逻辑替代fn:path(),避免依赖Saxon内部的QName处理:
<xsl:function name="my:merge" as="node()*"> <xsl:param name="node1" as="node()*"/> <xsl:param name="node2" as="node()*"/> <!-- 改用自定义路径构造逻辑替代fn:path() --> <xsl:for-each-group select="$node1, $node2" group-by="my:get-node-path(.)"> <xsl:copy> <xsl:sequence select="my:merge(@*, current-group()[2]/@*)"/> <xsl:sequence select="my:merge(node(), current-group()[2]/node())"/> </xsl:copy> </xsl:for-each-group> </xsl:function> <!-- 自定义获取节点路径的函数 --> <xsl:function name="my:get-node-path" as="xs:string"> <xsl:param name="node" as="node()"/> <xsl:choose> <xsl:when test="$node/parent::*"> <xsl:value-of select="concat(my:get-node-path($node/parent::*), '/', local-name($node))"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat('/', local-name($node))"/> </xsl:otherwise> </xsl:choose> </xsl:function>
步骤3:升级Saxon版本
Saxon 10.6是较旧的版本,可能存在已知的序列化相关bug。建议升级到最新的稳定版本(比如Saxon 12.x),新版本通常会修复这类兼容性问题。
不使用Serializer的转换方法
当然可以不用Serializer,直接使用StreamResult作为输出目标,代码更简洁:
Source xslt = new StreamSource(new StringReader(inputXSLT)); Source xml = new StreamSource(new StringReader(inputXML)); StringWriter transformedXML = new StringWriter(); Processor processor = new Processor(false); XsltCompiler compiler = processor.newXsltCompiler(); XsltExecutable stylesheet = compiler.compile(xslt); Xslt30Transformer transformer = stylesheet.load30(); // 直接使用StreamResult替代Serializer transformer.transform(xml, new StreamResult(transformedXML)); outputXML = transformedXML.toString();
如果需要设置输出格式(比如缩进、编码),可以直接在XSLT的<xsl:output>节点中配置,无需额外的Serializer配置。
内容的提问来源于stack exchange,提问作者Nijith
相关产品推荐
相关产品推荐

