使用XSLT转换XML时出现ArrayIndexOutOfBoundsException异常
解决Scala Play中XSLT移除命名空间时POST XML触发的异常问题
结合你在Scala Play应用里用javax.xml.transform做XSLT转换的场景,POST XML到端点触发异常,大概率和XSLT模板逻辑、Play请求解析、Transformer配置这几个环节有关,我给你拆解排查和解决方法:
1. 先排查XSLT模板的命名空间处理逻辑
很多时候异常是因为XSLT没正确匹配带命名空间的元素,导致转换过程中出现节点解析错误。给你一份经过验证的、可以移除所有元素/属性命名空间的XSLT模板:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 匹配所有元素,移除命名空间,保留本地名称 --> <xsl:template match="*"> <xsl:element name="{local-name()}"> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> <!-- 匹配所有属性,移除命名空间 --> <xsl:template match="@*"> <xsl:attribute name="{local-name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:template> <!-- 原样保留文本、注释、处理指令 --> <xsl:template match="text() | comment() | processing-instruction()"> <xsl:copy/> </xsl:template> </xsl:stylesheet>
如果你的XSLT模板里没有处理命名空间的匹配逻辑,或者用了错误的前缀匹配,就会导致Transformer无法正确处理节点,抛出TransformerException。
2. 检查Play控制器的XML请求解析方式
POST请求时,Play的请求体解析如果处理不当,会导致传入转换方法的XML字符串格式异常(比如额外转义、命名空间被自动添加)。正确的控制器写法应该是:
import play.api.mvc._ import scala.util.{Try, Success, Failure} import javax.xml.transform._ import javax.xml.transform.stream.{StreamSource, StreamResult} import java.io.{StringReader, StringWriter} class XmlController @Inject()(val controllerComponents: ControllerComponents) extends BaseController { def transformXml(xml: String, xslName: String): Try[String] = Try { val factory = TransformerFactory.newInstance() // 关键:从classpath加载XSL文件,避免路径错误 val xslStream = getClass.getClassLoader.getResourceAsStream(xslName) if (xslStream == null) throw new IllegalArgumentException(s"找不到指定的XSL文件: $xslName") // 配置安全属性,避免XXE攻击(Java 8+强制要求) factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD, "") factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "") val transformer = factory.newTransformer(new StreamSource(xslStream)) val source = new StreamSource(new StringReader(xml)) val resultWriter = new StringWriter() transformer.transform(source, new StreamResult(resultWriter)) resultWriter.toString } // 处理POST XML的端点 def handleXmlTransform = Action(parse.xml) { request => // 将Play解析的scala.xml.Elem转为字符串,确保格式正确 val rawXmlStr = request.body.toString() transformXml(rawXmlStr, "remove-namespace.xsl") match { case Success(transformedXml) => Ok(transformedXml).as("application/xml") case Failure(ex) => // 打印完整堆栈定位问题(生产环境换成日志记录) ex.printStackTrace() InternalServerError(s"转换失败: ${ex.getMessage}") } } }
这里要注意:
- 用
parse.xml解析请求体,确保Content-Type是application/xml或text/xml - 从classpath加载XSL文件时,要确认文件放在
resources目录下,路径正确 - 必须配置TransformerFactory的安全属性,否则Java 8+会抛出SecurityException
3. 定位具体异常类型
如果还是触发异常,一定要打印完整的异常堆栈(比如上面代码里的ex.printStackTrace()),常见的异常场景:
TransformerException:XSLT语法错误、节点匹配失败IllegalArgumentException:XSL文件找不到BodyParseException:POST的请求体不是合法XML,或者Content-Type不对SecurityException:TransformerFactory未配置安全属性,触发XXE防护限制
根据堆栈信息就能精准定位到问题环节。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

