在Java开发中处理XML文件时,经常会遇到XML文档头部包含DOCTYPE声明并引用外部DTD的情况,如果对应的DTD资源无法访问,解析器会尝试加载DTD导致解析卡顿甚至失败,禁用DOCTYPE验证可以有效提升解析效率。
问题背景
当XML文档包含如下DOCTYPE声明时,默认配置的XML解析器会尝试获取指定的DTD文件进行验证:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "http://example.org/schema.dtd">
<root>
<item>测试内容</item>
</root>
如果DTD地址不可达,解析器会阻塞等待超时,大幅增加解析耗时,甚至抛出连接异常,此时需要禁用DTD验证并忽略DOCTYPE处理。
基于DOM解析的实现方式
使用Java内置的DOM解析器时,需要通过设置DocumentBuilderFactory的相关特性来禁用DTD验证,具体步骤如下:
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class DomXmlParser {
public static void main(String[] args) {
try {
// 创建DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 禁用DTD验证相关特性
// 忽略外部DTD
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// 禁用DOCTYPE声明处理
factory.setFeature("http://xml.org/sax/features/validation", false);
// 忽略外部通用实体
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
// 忽略外部参数实体
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// 创建DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XML文件
Document document = builder.parse(new File("test.xml"));
// 后续处理文档逻辑
System.out.println("文档根节点名称:" + document.getDocumentElement().getNodeName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
基于SAX解析的实现方式
SAX解析是事件驱动型的解析方式,配置方式和DOM略有不同,需要通过XMLReader设置相关特性:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class SaxXmlParser {
public static void main(String[] args) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
// 关闭验证
factory.setValidating(false);
SAXParser parser = factory.newSAXParser();
// 获取XMLReader并设置特性
org.xml.sax.XMLReader xmlReader = parser.getXMLReader();
// 禁用外部DTD加载
xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// 禁用外部实体加载
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// 设置自定义处理器
xmlReader.setContentHandler(new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println("遇到元素:" + qName);
}
});
// 开始解析
xmlReader.parse(new org.xml.sax.InputSource(new File("test.xml").toURI().toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
基于StAX解析的实现方式
StAX是流式拉取解析模型,同样可以配置相关属性来忽略DTD验证:
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.FileInputStream;
public class StaxXmlParser {
public static void main(String[] args) {
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
// 禁用外部实体
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
// 禁用DTD
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("test.xml"));
while (reader.hasNext()) {
int event = reader.next();
if (event == XMLStreamConstants.START_ELEMENT) {
System.out.println("元素名称:" + reader.getLocalName());
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意事项
- 不同解析器对特性的支持可能存在差异,如果设置特性时抛出
ParserConfigurationException,需要检查解析器是否支持对应特性 - 禁用DTD验证后,XML文档的结构合法性将不会被校验,需要确保输入的XML文档格式符合预期,避免出现结构错误导致的后续处理异常
- 如果XML中使用了DTD定义的实体引用,禁用DTD后这些实体可能无法正常解析,需要根据实际业务场景判断是否需要保留实体解析逻辑
配置特性说明
以下是常用配置特性的作用说明:
| 特性URI | 作用说明 |
|---|---|
| http://xml.org/sax/features/validation | 控制是否开启XML验证,设置为false即禁用验证 |
| http://apache.org/xml/features/nonvalidating/load-external-dtd | 控制是否加载外部DTD文件,设置为false即忽略外部DTD |
| http://xml.org/sax/features/external-general-entities | 控制是否解析外部通用实体,设置为false可避免加载外部实体资源 |
| http://xml.org/sax/features/external-parameter-entities | 控制是否解析外部参数实体,设置为false可避免加载外部实体资源 |