jdom 或 dom4j读取xml文件时如何让dtd验证使用本地dtd文件或者不生效
一、写在所有之前:
因为dom4j和jdom在这个问题上处理的方法是一模一样的,只是一个是SAXBuilder 一个SAXReader,这里以jdom距离,至于dom4j只需要同理替换一下就可以了。
二、问题发生的情况
当你用jdom读取一个有dtd验证的xml文件,同时你的网络是不通的情况下。会出现以下错误:
1,代码如下
package dom;
import java.io.File;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
public class TestJdom {
public static void main(String[] args) {
File file = new File("./src/dom/aiwf_aiService.xml");
if (file.exists()) {
SAXBuilder builder = new SAXBuilder();
try {
Document doc = builder.build(file);
System.out.println(doc);
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("can not find xml file:"
+ file.getAbsolutePath());
}
}
}
2,xml文件
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.8//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd">
<workflow>
...............
</workflow>
3,错误如下
java.net.SocketException: Permission denied: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.Socket.connect(Socket.java:507)
at java.net.Socket.connect(Socket.java:457)
at sun.net.NetworkClient.doConnect(NetworkClient.java:157)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:365)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:477)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:214)
at sun.net.www.http.HttpClient.New(HttpClient.java:287)
at sun.net.www.http.HttpClient.New(HttpClient.java:299)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:792)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:744)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:669)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:913)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:973)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:905)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(XMLEntityManager.java:872)
at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(XMLDTDScannerImpl.java:282)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(XMLDocumentScannerImpl.java:1021)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:453)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:810)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:789)
at dom.TestJdom.main(TestJdom.java:26)
三、分析原因
当执行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
这是原文内容
How do I keep the DTD from loading? Even when I turn off validation the parser tries to load the DTD file.
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.
builder.setFeature(
"http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
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.
import org.xml.sax.*;
import java.io.*;
public class NoOpEntityResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId) {
return new InputSource(new StringBufferInputStream(""));
}
}
Then in the builder
builder.setEntityResolver(new NoOpEntityResolver());
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.
里边教我们定义个类
public class NoOpEntityResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId) {
return new InputSource(new StringBufferInputStream(""));
}
}
通过builder.setEntityResolver(new NoOpEntityResolver())方法来隐蔽起dtd验证器。这样就不会出错了。试了一下确实没问题了。但要知道xml没有dtd验证是不好的,我们是否能让它使用本地dtd验证呢。例如本文的oswork
我把验证文件workflow_2_8.dtd拷贝到本地,能否验证的时候用本地的呢?
3,用本地dtd验证
方法有两种
方法一、更改xml中的doctype声明,但是一般情况下更改这个是不好的。更改后就不是标准的了。
方法二、验证期替换
看到上边FAQ讲的方法你是否有什么灵感呢?
看看下边这段代码
package dom;
import java.io.File;
import java.io.IOException;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class TestJdom {
public static void main(String[] args) {
File file = new File("./src/dom/aiwf_aiService.xml");
if (file.exists()) {
SAXBuilder builder = new SAXBuilder();
builder.setValidation(false);
builder.setEntityResolver(new EntityResolver() {
public InputSource resolveEntity(String publicId,
String systemId) throws SAXException, IOException {
return new InputSource("./workflow_2_8.dtd");
}
});
try {
Document doc = builder.build(file);
System.out.println(doc);
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("can not find xml file:"
+ file.getAbsolutePath());
}
}
}
对了,同样是自己实现一个EntityResolver(这里用了匿名类),不同的是在里边使用本地的dtd验证
另外,匿名类内部,似乎这样写起来更顺眼些
InputStream stream = new FileInputStream( "your dtd file path" );
InputSource is = new InputSource(stream);
is.setPublicId(publicId);
is.setSystemId(systemId);
return is;
分享到:
相关推荐
NULL 博文链接:https://17173.iteye.com/blog/617640
dom4j帮助文档 官方API ...org.dom4j.dtd org.dom4j.io org.dom4j.jaxb org.dom4j.rule org.dom4j.rule.pattern org.dom4j.swing org.dom4j.tree org.dom4j.util org.dom4j.xpath org.dom4j.xpp
org.dom4j.dtd Classes to represent the DTD declarations. org.dom4j.io Provides input and output via SAX and DOM together with writing dom4j objects to streams as XML text. org.dom4j.jaxb org.dom4j....
使用dom4j解析含有这样dtd的xml文件时,它会从网络找到这个文件对xml文件进行校验。但是如果电脑断开网络的话就会出现错误说找不到dtd文件并解析失败。所以请使用以下方法...
log4j.dtd,用于控制日志信息输送的目的地,可设置为控制台、文件、GUI组件,亲测可用
log4j.dtd,可以控制日志信息输送的目的地是控制台、文件、GUI组件,希望对大家有帮助
最近用到xml 免不了学一点dtd的定义 而且也免不了解析xml了 我觉得dom4j挺好 就写了这么一个小类 有很多不精简 望大虾们指点。
Iexmltls是一个在IE浏览器下安装的用于检验xml约束是否正确的小插件
需要的DTD文件.rar需要的DTD文件.rar需要的DTD文件.rar
struts-2.0/2.1.dtd struts-config.dtd ........ web-app_2_3.dtd web-app_2_4.xsd
编写ibatis(mybatis)XML文件 一定需要的dtd文件
struts+spring+mybatis+hibernate的dtd文件
hibernate开发时,必需用到的DTD文件,该压缩包含有所需的三个文件:hibernate.properties,hibernate-configuration-3.0.dtd,hibernate-mapping-3.0.dtd。
开发ibatis所需的dtd文件,校验xml文件的dtd文件
Java的hibernate中xml自动提示dtd
mybatis的dtd文件,使xml书写更简单,提示更明了,希望对大家有帮助
(帮助文档大全)javaAPI帮助文档、dom4j帮助文档、Schema帮助文档、XPath文档、DTD帮助文档,java开发所需要的文档应有尽有。绝对超值。
mybatis源码级课件DTD文件jar包xml文件最全最新,mybatis源码级课件DTD文件jar包xml文件最全最新,mybatis源码级课件DTD文件jar包xml文件最全最新
Hibernate开发所需的dtd文件和jar包:包括基本的jar包和参考dtd文件。
mybatis所需的两个关键的dtd文件,没有的话xml文件不能用