XDocument 默认不支持添加 DTD,因其基于轻量级 LINQ to XML,为安全与简化模型而忽略 DTD 解析和序列化;需用 XmlWriter 配合 DoctypePublic/DoctypeSystem 输出简单 DOCTYPE,复杂内联 DTD 则须改用 XmlDocument。

C# 中的 XDocument 默认不支持直接添加 DTD(Document Type Definition)声明,因为 XDocument 基于 LINQ to XML,其设计目标是轻量、符合 W3C XML 1.0 核心规范,而DTD 属于已逐步被弃用的旧式 XML 特性,且 LINQ to XML 明确不提供对 DTD 的解析或序列化支持。
为什么 XDocument 不允许添加 DTD
Microsoft 官方文档明确指出:XDocument 在加载或保存时会忽略 DTD;调用 Save() 或 ToString() 时,DTD 不会被输出。这是出于安全(如防止 XXE 攻击)和简化模型的考虑。
- DTD 可能引入外部实体、参数实体等复杂特性,与 LINQ to XML 的不可变、纯内存 DOM 模型冲突
-
XDocument的Declaration属性仅支持XDeclaration(即 XML 声明,如),不包含 DTD - 尝试在节点中手动插入
..>字符串会导致解析失败或被自动过滤
绕过限制:用 XmlWriter 手动写入 DTD
若必须生成带 DTD 的 XML 字符串(例如对接遗留系统),可借助 XmlWriter 配合自定义设置,在写入根元素前显式写出 DTD 行:
var doc = new XDocument(
new XElement("root",
new XElement("child", "content")
)
);
using (var stringWriter = new StringWriter())
using (var writer = XmlWriter.Create(stringWriter, new XmlWriterSettings
{
OmitXmlDeclaration = false,
Indent = true,
DoctypeSystem = "my.dtd", // 可选:SYSTEM 公共 ID
DoctypePublic = "-//MyOrg//DTD MyDoc 1.0//EN" // 可选:PUBLIC ID
}))
{
// 手动写入 DOCTYPE(XmlWriter 会在 WriteStartDocument 后自动加)
writer.WriteStartDocument();
// 注意:DoctypePublic/DoctypeSystem 设置会触发 XmlWriter 自动写入 DOCTYPE 行
doc.Root?.WriteTo(writer);
writer.Flush();
string xmlWithDtd = stringWriter.ToString();
// 输出示例:
//
//
// content
}
⚠️ 注意:此方式依赖 XmlWriter 的 DoctypePublic / DoctypeSystem 设置,仅适用于简单 PUBLIC/SYSTEM 引用;无法写入内联 DTD(如包含 ENTITY 或 ATTLIST 定义),因为 XmlWriter 不支持嵌入式 DTD 内容。
需要完整内联 DTD?改用 XmlDocument
如果业务强依赖内联 DTD(例如定义实体 或元素约束),应切换到传统 XmlDocument:
-
XmlDocument支持CreateDocumentType()方法创建XmlDocumentType节点 - 可通过
AppendChild(docType)将 DTD 插入文档开头 - 支持完整 DTD 语法(包括内部子集、外部子集、参数实体等)
示例:
var doc = new XmlDocument();
var docType = doc.CreateDocumentType("root", "-//MyOrg//DTD MyDoc 1.0//EN", "my.dtd", null);
doc.AppendChild(docType);
var root = doc.CreateElement("root");
root.AppendChild(doc.CreateElement("child")).InnerText = "content";
doc.AppendChild(root);
string xml = doc.OuterXml; // 包含完整 DOCTYPE
总结与建议
对于绝大多数现代场景,避免使用 DTD —— 改用 XML Schema(XSD)或 JSON Schema 更安全、更易维护。若仅需兼容性输出简单 DOCTYPE 声明:
- 优先用
XmlWriter+DoctypePublic/DoctypeSystem(轻量、可控) - 禁用
XDocument.Save()直接输出,它不会保留 DTD - 涉及复杂 DTD 逻辑时,退回
XmlDocument是唯一可靠选择 - 生产环境注意关闭 DTD 解析(
XmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit),防范 XXE










