jdom dom4j解析xml不对dtd doctype进行验证(转)

时间:2023-03-08 22:43:37

一、写在所有之前:
因为dom4j和jdom在这个问题上处理的方法是一模一样的,只是一个是SAXBuilder 一个SAXReader,这里以jdom距离,至于dom4j只需要同理替换一下就可以了。
二、问题发生的情况
当你用jdom读取一个有dtd验证的xml文件,同时你的网络是不通的情况下。会出现以下错误:
1,代码如下

jdom dom4j解析xml不对dtd doctype进行验证(转)package dom;
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)import java.io.File;
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)import org.jdom.Document;
jdom dom4j解析xml不对dtd doctype进行验证(转)import org.jdom.input.SAXBuilder;
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)public class TestJdom {
jdom dom4j解析xml不对dtd doctype进行验证(转)    public static void main(String[] args) {
jdom dom4j解析xml不对dtd doctype进行验证(转)        File file = new File("./src/dom/aiwf_aiService.xml");
jdom dom4j解析xml不对dtd doctype进行验证(转)        if (file.exists()) {
jdom dom4j解析xml不对dtd doctype进行验证(转)            SAXBuilder builder = new SAXBuilder();
jdom dom4j解析xml不对dtd doctype进行验证(转)            try {
jdom dom4j解析xml不对dtd doctype进行验证(转)                Document doc = builder.build(file);
jdom dom4j解析xml不对dtd doctype进行验证(转)                System.out.println(doc);
jdom dom4j解析xml不对dtd doctype进行验证(转)            } catch (Exception e) {
jdom dom4j解析xml不对dtd doctype进行验证(转)                e.printStackTrace();
jdom dom4j解析xml不对dtd doctype进行验证(转)            }
jdom dom4j解析xml不对dtd doctype进行验证(转)        } else {
jdom dom4j解析xml不对dtd doctype进行验证(转)            System.out.println("can not find xml file:"
jdom dom4j解析xml不对dtd doctype进行验证(转)                    + file.getAbsolutePath());
jdom dom4j解析xml不对dtd doctype进行验证(转)        }
jdom dom4j解析xml不对dtd doctype进行验证(转)    }
jdom dom4j解析xml不对dtd doctype进行验证(转)}
jdom dom4j解析xml不对dtd doctype进行验证(转)

2,xml文件

jdom dom4j解析xml不对dtd doctype进行验证(转)<?xml version="1.0" encoding="GBK"?>
jdom dom4j解析xml不对dtd doctype进行验证(转)<!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.8//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd">
jdom dom4j解析xml不对dtd doctype进行验证(转)<workflow>
                ...............
jdom dom4j解析xml不对dtd doctype进行验证(转)</workflow>

3,错误如下

jdom dom4j解析xml不对dtd doctype进行验证(转)java.net.SocketException: Permission denied: connect
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.PlainSocketImpl.socketConnect(Native Method)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.Socket.connect(Socket.java:507)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at java.net.Socket.connect(Socket.java:457)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.NetworkClient.doConnect(NetworkClient.java:157)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.http.HttpClient.openServer(HttpClient.java:365)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.http.HttpClient.openServer(HttpClient.java:477)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.http.HttpClient.<init>(HttpClient.java:214)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.http.HttpClient.New(HttpClient.java:287)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.http.HttpClient.New(HttpClient.java:299)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:792)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:744)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:669)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:913)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:973)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:905)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(XMLEntityManager.java:872)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(XMLDTDScannerImpl.java:282)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(XMLDocumentScannerImpl.java:1021)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:453)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:810)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:789)
jdom dom4j解析xml不对dtd doctype进行验证(转)    at dom.TestJdom.main(TestJdom.java:26)
jdom dom4j解析xml不对dtd doctype进行验证(转)

三、分析原因
当执行build的时候jdom分析到
DOCTYPE workflow PUBLIC "-/OpenSymphony Group//DTD OSWorkflow 2.8//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd 
就会去读取http://www.opensymphony.com/osworkflow/workflow_2_8.dtd 这里的dtd文件来验证,但是因为网络是不通的所以就会报socket错误。

四、解决办法
1,最开始查看jdom api发现了这样一个方法
builder.setValidation(false);
这样可以让jdom不做验证,但是结果依然出问题,查了一下原因,说虽然不验证但是还是会下载
2,参照jdom网站的FAQ  http://www.jdom.org/docs/faq.html#a0100这是原文内容

jdom dom4j解析xml不对dtd doctype进行验证(转)How do I keep the DTD from loading? Even when I turn off validation the parser tries to load the DTD file.
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)Even when validation is turned off, an XML parser will by default load the external DTD file in order to parse the DTD for external entity declarations. Xerces has a feature to turn off this behavior named "http://apache.org/xml/features/nonvalidating/load-external-dtd" and if you know you're using Xerces you can set this feature on the builder.
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)builder.setFeature(
jdom dom4j解析xml不对dtd doctype进行验证(转)  "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)If you're using another parser like Crimson, your best bet is to set up an EntityResolver that resolves the DTD without actually reading the separate file.
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)import org.xml.sax.*;
jdom dom4j解析xml不对dtd doctype进行验证(转)import java.io.*;
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)public class NoOpEntityResolver implements EntityResolver {
jdom dom4j解析xml不对dtd doctype进行验证(转)  public InputSource resolveEntity(String publicId, String systemId) {
jdom dom4j解析xml不对dtd doctype进行验证(转)    return new InputSource(new StringBufferInputStream(""));
jdom dom4j解析xml不对dtd doctype进行验证(转)  }
jdom dom4j解析xml不对dtd doctype进行验证(转)}
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)Then in the builderjdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)builder.setEntityResolver(new NoOpEntityResolver());
jdom dom4j解析xml不对dtd doctype进行验证(转)
jdom dom4j解析xml不对dtd doctype进行验证(转)There is a downside to this approach. Any entities in the document will be resolved to the empty string, and will effectively disappear. If your document has entities, you need to setExpandEntities(false) code and ensure the EntityResolver only suppresses the DocType.
jdom dom4j解析xml不对dtd doctype进行验证(转)

里边教我们定义个类

jdom dom4j解析xml不对dtd doctype进行验证(转)public class NoOpEntityResolver implements EntityResolver {
jdom dom4j解析xml不对dtd doctype进行验证(转)  public InputSource resolveEntity(String publicId, String systemId) {
jdom dom4j解析xml不对dtd doctype进行验证(转)             return new InputSource(new StringBufferInputStream(""));
jdom dom4j解析xml不对dtd doctype进行验证(转)  }
jdom dom4j解析xml不对dtd doctype进行验证(转)}
jdom dom4j解析xml不对dtd doctype进行验证(转)

通过builder.setEntityResolver(new NoOpEntityResolver())方法来隐蔽起dtd验证器。这样就不会出错了。试了一下确实没问题了。但要知道xml没有dtd验证是不好的,我们是否能让它使用本地dtd验证呢。例如本文的oswork
我把验证文件workflow_2_8.dtd拷贝到本地,能否验证的时候用本地的呢? 
3,用本地dtd验证
方法有两种
方法一、更改xml中的doctype声明,但是一般情况下更改这个是不好的。更改后就不是标准的了。
方法二、验证期替换
public Document load(String file) throws JDOMException, IOException {
       try {
  SAXBuilder sax = new SAXBuilder();
        sax.setValidation(false);
        sax.setEntityResolver(new EntityResolver() {
            public InputSource resolveEntity(String publicId,String systemId) throws SAXException, IOException {
                    /*InputSource is = new InputSource(new FileInputStream(System.getProperty("user.dir")+"//web-app_2_3.dtd"));
                    is.setPublicId(publicId);
                    is.setSystemId(systemId);
                    return is;

*/
                 return new InputSource(new FileInputStream(""));
            }
        });
  return sax.build(file);
       } catch ( Exception e )  {
           e.printStackTrace();
           return null;           
       }
 }

http://blog.csdn.net/youlianying/article/details/5908335