解决java web容器解析xml不能找到DTD问题

项目创建了一个dtd文件,放在tomcat webapp class 目录下面,xml也位于class目录下,xml 的 dtd引用直接为dtd文件名。但是解析时并不是根据xml的相对路径取找dtd文件,而是在执行目录下(也就是tomcat bin目录)下查找,当然找不到。(给xml的dtd引用加上斜杠表示相对路径也没用)
  在网上找了很久,终于有了一个解决方案,与大家共享:

       用DOM解析的时候,我们可以自定义实体的解析器(EntityResolver),而不是使用默认的实体解析器。(默认的解析器会根据实际的url进行读取)
from:http://www.javayou.com/diary/623


般比较正式的XML信息中都会包含对应的DTD声明,用来定义该XML文档中的格式,例如WEB项目中用到的web.xml,例如struts的配置文件struts-config.xml,下面是web.xml中用到的DTD信息:

 

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
       "
http://java.sun.com/dtd/web-app_2_3.dtd">

当我们用DOM或者Digester来解析这个XML的时候,如果当前计算机已联网那么解析的速度比较慢,如果当前计算机未联网则会报无法连接主机的异常。这是因为XML的解析器需要读取dtd的内容,而这个dtd文件是存在于互联网的某台主机上的,因此问题就在于:如果我们的计算机不能保证时时都连在网上,那么怎么老保证解析过程不出错呢?下面我们分别就两种不同的解析方法进行说明。

1. 使用DOM解析

使用DOM解析的时候,我们可以自定义实体的解析器(EntityResolver),而不是使用默认的实体解析器,因为默认的解析器会根据实际的url进行读取,下面是一段如何来解析web.xml的代码:

 

private static void parse(InputStream in) throws Exception{
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  DocumentBuilder db = dbf.newDocumentBuilder();
  
db.setEntityResolver(new EntityResolver(){
   public InputSource resolveEntity(String publicId, String systemId)
    throws SAXException, IOException{
          for (int i = 0; i < registrations.length; i += 2) {
           if(publicId.equals(registrations[i])){
            String dtd_uri = registrations[i+1];
               InputStream dtd_stream = this.getClass().getResourceAsStream(dtd_uri);
      return new InputSource(dtd_stream);
           }
          }
    return null;
   }});

  Document doc = db.parse(in);
  Element root = doc.getDocumentElement();
  NodeList nodes = root.getElementsByTagName("servlet-mapping");
  int nodec = nodes.getLength();
  for(int i=0;i<nodec;i++){
   Element node = (Element)nodes.item(i);
   Element servlet_name = (Element)node.getElementsByTagName("servlet-name").item(0);
   Element url_pattern = (Element)node.getElementsByTagName("url-pattern").item(0);
   
   String sn = servlet_name.getFirstChild().getNodeValue();
   String up = url_pattern.getFirstChild().getNodeValue();
   System.out.println(sn+"="+up);
  }
 }

private final static String registrations[] = {
        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",
        "/web-app_2_2.dtd",
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
        "/web-app_2_3.dtd"
};

这段代码中把本来从http://java.sun.com/dtd/web-app_2_3.dtd地址中读取dtd的信息改为了从当前类路径中读取,也就是/web-app_2_3.dtd,通过重新定义InputSource来返回/web- app_2_3.dtd数据流,从而让XML解析器不从网上获取DTD信息。当然,这样做的前提是事先必须把dtd文件保存在类路径所在的目录中,以便自定义的EntityResolver可以读取到。

2. 使用Digester解析

使用Digester解析时原理也是一样的,代码有所不同而已:

 

Digester digester = new Digester();
digester.push(this);
digester.setNamespaceAware(true);
digester.setValidating(false);

// Register our local copy of the DTDs that we can find
for (int i = 0; i < registrations.length; i += 2) {
    URL url = this.getClass().getResource(registrations[i+1]);
    if (url != null)
        digester.register(registrations[i], url.toString());            
}

其中registrations与前一段代码相同。

通过上面两种处理办法以后,XML的解析器就不再从网上获取dtd文件,这也是为什么Tomcat包括struts项目可以在计算机没有联网下工作的原因,因为Tomcat需要解析web.xml,struts需要解析web.xml以及struts-config.xml,而这两个文件的dtd恰恰都是通过一个url给出的。

需要说明一点的是,这样做的目的并不是说屏蔽了DTD的作用,毕竟DTD是用来验证XML语法的,如果去掉了,验证的功能就没有了,因此DTD不能去掉。这样做的真正目的是在保留DTD功能外使应用程序在无网络连接的情况下也可以运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值