Java 如何从TransformerException中提取有用信息

Java 如何从TransformerException中提取有用信息,java,exception,xslt,Java,Exception,Xslt,我正在使用javax.xml.transform.*进行XSLT转换。由于要使用的xslt文件来自外部世界,因此该文件中可能存在错误,我将向用户返回一些有意义的响应 虽然我可以很容易地捕获转换异常s,但我发现没有办法从中获得足够的信息。例如,如果有一个标记要被一个结束标记终止,printStackTrace()会给出一条疤痕消息 javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet

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

虽然我可以很容易地捕获
转换异常
s,但我发现没有办法从中获得足够的信息。例如,如果有一个标记要被一个结束标记终止,
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'
[致命错误]:259:155:元素类型“sometag”必须由匹配的结束标记“”终止。
错误:“元素类型“sometag”必须由匹配的结束标记“”终止。”
致命错误:“无法编译样式表”
这正是我想要的。不幸的是,由于这是一个web应用程序,用户无法看到这一点

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

将您自己的错误消息放在您的
Transformer
实例上,如下所示:

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
}

您可以将System.out配置为写入自己的OutputStream。 使用ErrorListener不会捕获所有输出。 如果使用线程,可以查看此处()以避免更改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;
    }
}

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

XML解析器的错误可能存在于包装异常中。出于历史原因,TransformerConfigurationException同时提供getException()和getCause()来访问包装的异常,这两种异常都值得检查

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


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

您真的不能。如果要运行验证步骤……原因异常中不是原因吗?@agad不幸的是
getCause()
调用返回
null
@earthine关于getOriginalException()的情况如何?@agad在
TransformerException
TransformerConfigurationException
中都没有这样的方法。它捕捉不到什么输出?他想要的只是错误。这真是太过分了。另外,如果他的服务器在运行时输出了其他内容,因为它是一个多线程系统,该怎么办?用户将在收到的错误消息中看到该输出。这不是一个好的解决方案。取决于变压器引擎的一些(有用的)输出进入System.out。不幸的是,ErrorListener没有捕获到这一点。要避免捕获其他线程输出,请使用maiaco解决方案。太好了。
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;
    }
}