Java 解析 XML 时如何屏蔽掉 “[Fatal Error]” 的输出

我们在用 Java 解析 XML,当文档不是一个合法的 XML 时,可能会收到 [Fatal Error] 的控制台输出,即使把整个代码都 catch 住,仍然不能抑制住 [Fatal Error] 的信息输出。比如常见到这样的输出:

[Fatal Error] :1:1: Content is not allowed in prolog.

为什么不能禁掉它呢,本来 catch 了异常对程序已经有了很好的保护,想眼不见心不烦,但还是避之不及。

因为,因为这个 XML 解析器用 System.error.print() 输出来了,当然你可以用 System.setErr(PrintStream) 重定向掉错误输出,但不现实,波及面太大。我们需要找到源头,首先交代解决方案就是覆盖掉默认的 ErrorHandler。

看下这段 XML 解析代码:

上面的代码就会输出

[Fatal Error] :1:1: Content is not allowed in prolog.

如果 xml 的值是空字符串 "",输出为

[Fatal Error] :-1:-1: Premature end of file.

如果 xml 的值是 "<s>ss&Emal</s>",输出为

[Fatal Error] :1:12: The reference to entity "Email" must end with the ';' delimiter.

是不是对上面的错误输出很熟悉啊。

那就要看 DocumentBuilderFactory.newInstance.newDocumentBuilder 的两个过程,首先看 DocumentBuilderFactory.newInstance() 方法,见

http://docs.oracle.com/javase/7/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#DocumentBuilderFactory()

看到它依次以四种方式找到 DocumentBuilderFactory 的实现类

  1. 系统属性 javax.xml.parsers.DocumentBuilderFactory
  2. JRE 目录下的属性文件 "lib/jaxp.properties" 中的  javax.xml.parsers.DocumentBuilderFactory
  3. SPI 形式,classpath 下加载 META-INF/services/javax.xml.parsers.DocumentBuilderFactory, 一般在 jar 包中
  4. 平台默认的 DocumentBuilderFactory 实例

默认的 JDK7 环境中 DocumentBuilderFactory.newInstance 是 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl,

DocumentBuilderFactory.newInstance.newDocumentBuilder 是 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl

比如在 xercesImpl-2.11.0.jar 包下就有文件 META-INF/services/javax.xml.parsers.DocumentBuilderFactory, 内容是

org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, 相应的  DocumentBuilder 是 org.apache.xerces.jaxp.DocumentBuilderImpl

在实例化 DocumentBuilderImpl 时并没有给 DocumentBuilder 或 DOMParser 设置 ErrorHandler,而是在解析发现问题是设置上 ErrorHandler,然后输出错误,见

java-xml-error-handler

上面设置了 DefaultErrorHandler, 点击 DefaultErrorHandler 看它的实现。它在 warning 和 error 时只打印错误,fatal 时除打印还抛出了异常。打印目的地是 System.err

但是我们可以设置一个 java.xml.parsers.DocumentBuilderFactory 系统属性从而使用自定义的 DocumentBuilderFactory 实现,在自己的 DocumentBuilderFactory 中初始化 DocumentBuilder 时设置自己的 ErrorHandler。

现在我们来实现自定义的 DocumentBuilderFactory:

CustomDocumentBuilderFactory.java

然后重写获得 DocumentBuilder 部分代码为

再次执行就只看到控制台的输出为

TestDocumentBuilderFactoryImpl Caught the XML fatal error: Premature end of file.

表明我们已经成功捕获到了 fataError 了,怎么输出这个错误是可控制的了。

在不同的环境下,例如 Tomcat 中可以去查看下默认的 DocumentBuilderFactory 实现,然后自定义的 Factory 就可以继承它,只为设置自己的 ErrorHandler,同时也保证了不破坏该环境下原有其他的行为。

比如在 Play2 中 DocumentBuilderFactory 实现是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, DocumentBuilder 实现是 org.apache.xerces.jaxp.DocumentBuilderImpl,它们来自 xercesImpl 包,这时候我们自定义的 DocumentBuilderFactory 就可以继承自 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, 其中设置自己的 ErrorHandler。

另外,我们也可以通过调用 DefaultErrorHandler 的另一个构造方法 DefaultErrorHandler(PrintWriter out) 来创造实例,通过 传递一个 PrintWriter 实例来接受输出,这样也能控制不把 Fatal Error 输出到控制台上。

本文链接 https://yanbin.blog/java-parse-xml-suppress-fatal-error-output/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments