使用XSLT转换XML时出现ArrayIndexOutOfBoundsException

时间:2018-04-19 10:34:24

标签: java xml scala xslt playframework

我在Scala Play应用程序中使用Java javax.xml.transform库对某些XML执行简单的XSLT转换。我试图从其中一个元素中删除命名空间,但是当我将XML发送到执行转换的端点时,我遇到了异常。

我编写的转换方法如下:

def transformXml(xml: String, xslName: String): Try[String] = {
  Try {
    // Create transformer factory
    val factory: TransformerFactory = TransformerFactory.newInstance()

    // Use the factory to create a template containing the xsl file
    val template: Templates = factory.newTemplates(new StreamSource(new FileInputStream(s"app/xsl/$xslName.xsl")))

    // Use the template to create a transformer
    val xformer: Transformer = template.newTransformer()

    // Prepare the input for transformation
    val input: Source = new StreamSource(new StringReader(xml))

    // Prepare the output for transformation result
    val outputBuffer: Writer = new StringWriter
    val output: javax.xml.transform.Result = new StreamResult(outputBuffer)

    // Apply the xslt transformation to the input and store the result in the output
    xformer.transform(input, output)

    // Return the transformed XML
    outputBuffer.toString
  }
}

通过在我的代码中添加println,我推断它实际上在xformer.transform(input, output)行失败了。我传入的XML和我用来转换的XSL文件如下:

<?xml version="1.0"?>
<Message xmlns="http://foo.bar" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
    <EnvelopeVersion>2.0</EnvelopeVersion>
    <Header>
        <MessageDetails>
            ...
            ...
            ...
        </MessageDetails>
        <SenderDetails/>
    </Header>
    <OtherDetails>
        <Keys/>
    </OtherDetails>
    <Body>
    </Body>
</Message>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:param name="ancestralNamespace" select="namespace-uri(/*[1])"/>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
                <xsl:with-param name="ancestralNamespace" select="$ancestralNamespace"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[contains(namespace-uri(),'foo.bar')]">
        <xsl:param name="ancestralNamespace" select="namespace-uri(..)"/>
        <xsl:element name="{local-name()}" namespace="">
            <xsl:apply-templates select="@*|node()">
                <xsl:with-param name="ancestralNamespace" select="$ancestralNamespace"/>
            </xsl:apply-templates>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

我的预期输出是:

<?xml version="1.0"?>
    <Message>
        <EnvelopeVersion>2.0</EnvelopeVersion>
        <Header>
            <MessageDetails>
                ...
                ...
                ...
            </MessageDetails>
            <SenderDetails/>
        </Header>
        <OtherDetails>
            <Keys/>
        </OtherDetails>
        <Body>
        </Body>
    </Message>

我从发送POST请求到我的端点的错误是:

{
    "statusCode": 500,
    "message": "javax.xml.transform.TransformerException: java.lang.ArrayIndexOutOfBoundsException: -1"
}

我对XSLT没有多少经验,并且从其他人那里继承了这段代码来尝试调试,所以如果有XML / XSLT经验的人能给我一些帮助,我将非常感激。令人困惑的是,我遇到这个问题的人已经使用这种方法编写了单元测试(发送我的示例XML并获得预期的XML)并且他们通过了所以我不知道接下来要去哪里看。

1 个答案:

答案 0 :(得分:2)

正确,经过几个小时的调试和对此的烦恼,我找到了解决方案!

我的Play应用程序使用的默认转换器以不同的方式处理XSLT,并且在<xsl:param name="ancestralNamespace" select="namespace-uri(/*[1])"/>行感到困惑。解决我的问题是使用不同的变压器。我发现工作的是Xalan(版本2.7.2),在将其导入我的项目构建文件后,我点击端点并且转换成功。

要导入我发现的版本,请将以下内容添加到您的版本中:

"xalan" % "xalan" % "2.7.2" % "runtime"

我相信&#34;运行时&#34; section是最重要的部分,因为它似乎覆盖了应用程序通常使用的内容。我猜测我的测试通过但我的端点失败的原因是Scala Test以不同的配置运行到运行时。我的代码没有别的东西可以改变。

我希望这有助于阻止其他人遇到这个(诚然相当独特)的错误!我最终在2002年之前在无数的论坛上拖网,然后尝试了不同的运行时配置。