带DTD的XML文件在头部会包含文档类型定义,用于约束XML的元素结构、属性规则等内容,解析这类文件时需要根据解析器的特性调整配置,否则可能触发DTD校验或者外部实体加载的问题。

带DTD的XML结构说明
带DTD的XML通常在文件开头包含<!DOCTYPE>声明,示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [
<!ELEMENT user (name, age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ATTLIST user id CDATA #REQUIRED>
]>
<user id="1001">
<name>张三</name>
<age>25</age>
</user>
上述示例中DTD定义了user元素必须包含name和age子元素,user元素必须有id属性,解析时如果XML不符合这些规则,默认开启校验的解析器会直接抛出异常。
使用DOM方式解析带DTD的XML
Java中可以使用DocumentBuilder类实现DOM解析,解析带DTD的XML时可以通过配置DocumentBuilderFactory来控制DTD的处理逻辑。
基础解析示例(开启DTD校验)
如果希望按照DTD规则校验XML内容,可以使用默认配置进行解析:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class DomParseWithDTD {
public static void main(String[] args) throws Exception {
// 创建DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 默认开启DTD校验,不需要额外配置
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析带DTD的XML文件,如果XML不符合DTD规则会抛出异常
Document document = builder.parse("user_with_dtd.xml");
// 获取根元素
Element root = document.getDocumentElement();
System.out.println("根元素名称:" + root.getTagName());
// 获取user的id属性
String userId = root.getAttribute("id");
System.out.println("用户id:" + userId);
// 获取name元素内容
NodeList nameNodes = root.getElementsByTagName("name");
String name = nameNodes.item(0).getTextContent();
System.out.println("用户名称:" + name);
// 获取age元素内容
NodeList ageNodes = root.getElementsByTagName("age");
String age = ageNodes.item(0).getTextContent();
System.out.println("用户年龄:" + age);
}
}
忽略DTD校验的解析配置
如果不希望进行DTD校验,可以关闭DocumentBuilderFactory的相关特性:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class DomParseIgnoreDTD {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 关闭DTD校验特性
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setFeature("http://xml.org/sax/features/validation", false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("user_with_dtd.xml");
Element root = document.getDocumentElement();
System.out.println("解析成功,根元素:" + root.getTagName());
}
}
使用SAX方式解析带DTD的XML
SAX是事件驱动的解析方式,内存占用更低,同样可以通过配置SAXParserFactory来处理DTD。
基础SAX解析示例
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 SaxParseWithDTD {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
// 开启DTD校验
factory.setValidating(true);
SAXParser parser = factory.newSAXParser();
// 自定义处理器处理解析事件
DefaultHandler handler = new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("user".equals(qName)) {
System.out.println("用户id:" + attributes.getValue("id"));
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String content = new String(ch, start, length).trim();
if (!content.isEmpty()) {
System.out.println("元素内容:" + content);
}
}
};
parser.parse(new File("user_with_dtd.xml"), handler);
}
}
忽略DTD校验的SAX配置
如果不需要校验DTD,可以关闭SAXParser的校验特性:
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class SaxParseIgnoreDTD {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
// 关闭校验
factory.setValidating(false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser parser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println("遇到元素:" + qName);
}
};
parser.parse(new File("user_with_dtd.xml"), handler);
}
}
解析带DTD的XML注意事项
- 如果DTD中引用了外部实体,默认解析可能会触发网络请求或者安全漏洞,生产环境建议关闭外部DTD加载特性。
- 开启DTD校验时,XML文件必须符合DTD定义的结构,否则解析会直接失败。
- 不同解析器的特性配置名称可能存在差异,需要根据使用的解析库调整对应的配置参数。
- 如果不需要校验XML结构,优先关闭DTD校验可以提升解析效率,减少不必要的资源消耗。
两种解析方式对比
| 解析方式 | 内存占用 | 是否支持随机访问 | DTD校验配置复杂度 |
|---|---|---|---|
| DOM解析 | 较高,会加载整个文档到内存 | 支持,可以随意访问任意节点 | 较低,通过Factory特性配置即可 |
| SAX解析 | 较低,逐行解析不加载全文档 | 不支持,只能顺序读取 | 中等,需要同时配置Factory和特性 |
开发者可以根据实际的XML文件大小、是否需要校验、是否需要随机访问节点等需求,选择合适的解析方式和对应的DTD处理配置。
XMLDTDXML解析SAXParserDocumentBuilder修改时间:2026-07-18 08:12:31