获取xml命名空间(不触发UnknownHostException)

时间:2011-08-10 20:14:21

标签: java xml sax xml-namespaces xerces

我有一些Java代码使用SAX确定xml文档的根级元素的命名空间。如果命名空间为“http://sbgn.org/libsbgn/pd/0.1”,则应返回版本1.如果命名空间为“http://sbgn.org/libsbgn/0.2”,则版本应为2。因此,所有代码都会读取第一个元素,并根据命名空间设置变量。这是代码:

private static class VersionHandler extends DefaultHandler
{
    private int version = -1;

    @Override
    public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException
    {
        if ("sbgn".equals (qName))
        {
            System.out.println (uri);
            if ("http://sbgn.org/libsbgn/0.2".equals(uri))
            {
                version = 2;
            } 
            else if ("http://sbgn.org/libsbgn/pd/0.1".equals(uri))
            {
                version = 1;
            } 
            else
            {
                version = -1;
            }
        }
    }

    public int getVersion() { return version; }
};

public static int getVersion(File file) throws SAXException, FileNotFoundException, IOException
{
    XMLReader xr;   
    xr = XMLReaderFactory.createXMLReader();

    VersionHandler versionHandler = new VersionHandler();

    xr.setContentHandler(versionHandler);
    xr.setErrorHandler(versionHandler);
    xr.parse(new InputSource(
        InputStreamToReader.inputStreamToReader(
            new FileInputStream (file))));

    return versionHandler.getVersion();
}   

这有效,但有两个问题:

  1. 这是低效的,因为即使只需要第一个元素,整个文档也会被解析。
  2. 更重要的是,此代码有时(显然取决于防火墙配置)会触发UnknownHostException,如下所示:
  3.     java.net.UnknownHostException: www.w3.org 
        at java.net.PlainSocketImpl.connect(Unknown Source)
        at java.net.SocksSocketImpl.connect(Unknown Source)
        at java.net.Socket.connect(Unknown Source)
        at java.net.Socket.connect(Unknown Source)
        at sun.net.NetworkClient.doConnect(Unknown Source)
        at sun.net.www.http.HttpClient.openServer(Unknown Source)
        at sun.net.www.http.HttpClient.openServer(Unknown Source)
        at sun.net.www.http.HttpClient.(Unknown Source)
        at sun.net.www.http.HttpClient.New(Unknown Source)
        at sun.net.www.http.HttpClient.New(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown
        Source)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.next(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown
        Source)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown
        Source)
        at
        com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown
        Source)
        at org.sbgn.SbgnVersionFinder.getVersion(SbgnVersionFinder.java:57)
    

    所以我的问题是:

    1. 显然这段代码正在连接到互联网。我怎么能避免这种情况?除了导致防火墙出现问题外,它也不必要地慢了。
    2. 为什么是否连接到互联网?请帮助我理解这里的逻辑,应该绝对没有必要。
    3. 是否有更有效的方法来确定xml文档的根元素的命名空间?
    4. 编辑:这是我尝试以这种方式解析的示例文档的链接:https://libsbgn.svn.sourceforge.net/svnroot/libsbgn/trunk/test-files/PD/adh.sbgn

      Edit2:关于这个bug的解决方案的说明:事实上问题是因为正在解析错误的文档而不是目标文档而被触发,我正在解析实际上参考www.w3的XHMTML文档.ORG。当然解决方案是使用正确的文档。不过,我发现添加这一行很有用:

       xr.setEntityResolver(null);
      

      为了防止xerces在完全没必要时通过互联网。

2 个答案:

答案 0 :(得分:2)

我相信你需要设置实体解析器。请参阅javadoc。此外,这article似乎也很重要。

答案 1 :(得分:2)

它可能连接到互联网,因为您的文档指的是W3C网站上的DTD或其他外部实体。今年早些时候,W3C因为无法处理流量而停止提供这些文件。

一旦您看到了需要查看的文档,就可以通过从其中一个回调中抛出SAXException来解决读取整个文档的问题。确保在调用XMLReader.parse()方法的代码中将此异常与解析器本身抛出的异常区分开(例如,您可以将SAXException子类化:尽管并非所有解析器都会保持原始异常不变,您可能需要进行实验。 )