为什么 JDOM 的 SAXBuilder 会导致 java.net.SocketException?

Why does JDOM´s SAXBuilder cause a java.net.SocketException?

我有一个 JDOM 1.1 问题,我找不到解决方案,更不用说原因了(我已经搜索了很长时间...)。我有一个设置非常简单的小测试程序:

  1. 创建一个新的 File 对象,该对象已经存在并且我打算解析。这是一个 XHTML 文件。
  2. 创建一个新的 SAXBuilder 并将验证设置为 false
  3. 创建一个新的 Document 对象,该对象应由 SAXBuilder
  4. 填充

问题与我的问题

不知何故,builder.build(file) 导致 java.net.SocketException 消息 Permission denied: connect。我不明白为什么它甚至需要一个套接字连接,除了验证,我在 SAXBuilder 的构造函数中将其设置为 false。所以,我的问题是:谁能告诉我导致该异常的原因是什么?更具体地说,它会尝试连接到哪里,我该如何阻止它?

谢谢!

JAVA代码

public static void main(String[] args) {
File file = new File("C:\Users\ABC\Desktop\test.xhtml");
SAXBuilder builder = new SAXBuilder(false);
try {
  Document document = builder.build(file);
  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
  outputter.output(document, System.out);
} catch (JDOMException e) {
  e.printStackTrace();
} catch (IOException e) {
  e.printStackTrace();
}
}

XHTML 文件

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Title of document</title>
</head>

<body>
  some content
</body>

</html>

堆栈跟踪

java.net.SocketException: Permission denied: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:211)
at sun.net.www.http.HttpClient.New(HttpClient.java:308)
at sun.net.www.http.HttpClient.New(HttpClient.java:326)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:996)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:932)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:850)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1300)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:637)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1290)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(XMLEntityManager.java:1257)
at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(XMLDTDScannerImpl.java:263)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(XMLDocumentScannerImpl.java:1164)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.next(XMLDocumentScannerImpl.java:1050)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:964)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:649)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:489)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:847)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:826)
at test.xmlparser.Main.main(Main.java:51)

问题是 DTD 用于验证 XHTML 文档。正如您在评论中发现的那样。

在 XML 中,DTD 不仅用于验证 XHTML 文档结构,还用于提供任何需要扩展的实体引用(想想 nbsp; 等)。

之所以在知道您已经有了答案的情况下回答这个问题,是因为您的答案是部分答案,并不是一个好的解决方案。删除 XHTML 文档的文档类型将在以后引起其他问题。

不幸的是,正确的解决方案是让 DTD 文档可用于您的解析器。对于标准 XML 解析,典型的方法是从 URL 中提取 DTD(这是 JDOM 在这里试图做的——实际上 xerces 正试图这样做,因为 JDOM 要求它解析文档)。如果您没有网络连接,那将非常困难,正如您所发现的那样。但是,即使您有网络连接,如果 XML 是 XHTML,您也会遇到双重问题 - W3C 人员故意破坏您的代码!!!

您应该查看一些参考资料:

  1. W3C's excessive DTD block
  2. How to bypass the request to the internet for DTD and some instructions on IBM Developerworks
  3. 使用目录:XML-Catalog
  4. 我不久前开始的一个小项目 "behaves well" 并缓存文档:Resolver <-- 这对你不是很有用,因为你没有网络,但它有一些有关问题的文档。

如果您知道要如何解析 DTD,则可以将 EntityResolver 提供给 JDOM SAXBuilder:setEntityResolver()