4

我正在使用javax.xml.transform.*XSLT 转换。由于要使用的 xslt 文件来自外部世界,因此该文件中可能存在错误,我将向用户返回一些有意义的响应。

虽然我可以很容易地捕捉到TransformationExceptions,但我发现无法从中获取足够的信息。例如,如果有一个标签要被一个结束标签终止,printStackTrace()则给出疤痕信息

javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(Unknown Source)
    ... (100 lines)

并且getMessage()只给

Could not compile stylesheet

他们都没有给出错误的真正原因。

我注意到在 Eclipse 测试控制台中我可以看到以下内容

[Fatal Error] :259:155: The element type "sometag" must be terminated by the matching end-tag "</sometag>".
ERROR:  'The element type "sometag" must be terminated by the matching end-tag "</sometag>".'
FATAL ERROR:  'Could not compile stylesheet'

这正是我想要的。不幸的是,由于这是一个 Web 应用程序,用户无法看到它。

如何向用户显示正确的错误消息?

4

3 回答 3

1

使用将您自己的实例放在ErrorListener您的Transformer实例上Transformer.setErrorListener,如下所示:

final List<TransformationException> errors = new ArrayList<TransformationException>();
Transformer transformer = ... ;
transformer.setErrorListener(new ErrorListener() {

    @Override
    public void error(TransformerException exception) {
        errors.add(exception);
    }

    @Override
    public void fatalError(TransformerException exception) {
        errors.add(exception);
    }

    @Override
    public void warning(TransformerException exception) {
        // handle warnings as well if you want them
    }
});

// Any other transformer setup

Source xmlSource = ... ;
Result outputTarget = ... ;
try {
    transformer.transform(xmlSource, outputTarget);
} catch (TransformerException e) {
    errors.add(e); // Just in case one is thrown that isn't handled
}
if (!errors.isEmpty()) {
    // Handle errors
} else {
    // Handle output since there were no errors
}

这会将发生的所有错误记录到errors列表中,然后您可以使用这些错误的消息来获得您想要的。这有一个额外的好处,它会在错误发生后尝试恢复转换。如果这导致任何问题,只需通过执行以下操作重新引发异常:

@Override
public void error(TransformerException exception) throws TransformationException {
    errors.add(exception);
    throw exception;
}

@Override
public void fatalError(TransformerException exception) throws TransformationException {
    errors.add(exception);
    throw exception;
}
于 2013-09-05T06:30:21.740 回答
0

首先,任何解决方案都可能取决于您选择的 XSLT 处理器。JAXP 接口的不同实现很可能在它们生成的异常中提供不同的信息。

来自 XML 解析器的错误可能在包装的异常中可用。由于历史原因,TransformerConfigurationException 提供了 getException() 和 getCause() 来访问包装的异常,可能值得同时检查它们。

或者,信息可能是在对 ErrorListener 的单独调用中提供的。

最后,XML 解析器(而不是 XSLT 处理器)检测到这个特定的错误,因此在第一个实例中,它将由解析器处理。设置解析器的 ErrorHandler 并在该级别捕获解析错误可能是值得的。如果您想显式控制转换使用的 XML 解析器,请使用其 XMLReader 已适当初始化的 SAXSource。

于 2013-09-05T08:29:54.790 回答
-1

您可以配置 System.out 以写入您自己的 OutputStream。使用 ErrorListener 不会捕获所有输出。如果您使用线程,您可以查看此处(http://maiaco.com/articles/java/threadOut.php)以避免 System.out 更改为其他线程。

例子

public final class XslUtilities {
    private XslUtilities() {
        // only static methods
    }

    public static class ConvertWithXslException extends Exception {
        public ConvertWithXslException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    public static String convertWithXsl(String input, String xsl) throws ConvertWithXslException {
        ByteArrayOutputStream systemOutByteArrayOutputStream = new ByteArrayOutputStream();
        PrintStream oldSystemOutPrintStream = System.out;
        System.setOut(new PrintStream(systemOutByteArrayOutputStream));

        ByteArrayOutputStream systemErrByteArrayOutputStream = new ByteArrayOutputStream();
        PrintStream oldSystemErrPrintStream = System.err;
        System.setErr(new PrintStream(systemErrByteArrayOutputStream));

        String resultXml;
        try {
            System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer(new StreamSource(new StringReader(xsl)));
            StringWriter stringWriter = new StringWriter();
            transformer.transform(new StreamSource(new StringReader(input)), new StreamResult(stringWriter));
            resultXml = stringWriter.toString();
        } catch (TransformerException e) {
            System.out.flush();
            final String systemOut = systemOutByteArrayOutputStream.toString();
            System.err.flush();
            final String systemErr = systemErrByteArrayOutputStream.toString();

            throw new ConvertWithXslException("TransformerException - " + e.getMessageAndLocation()
                            + (systemOut.length() > 0 ? ("\nSystem.out:" + systemOut) : "")
                            + (systemErr.length() > 0 ? ("\nSystem.err:" + systemErr) : ""), e);
        } finally {
            System.setOut(oldSystemOutPrintStream);
            System.setErr(oldSystemErrPrintStream);
        }

        return resultXml;
    }
}
于 2013-09-05T06:37:30.077 回答