新项目应优先选用XDocument。因其语法更直观、支持LINQ查询、自动类型转换、命名空间处理更安全、默认忽略空白节点、提供异步加载,并能与XmlReader协作处理大文件。

选 XDocument 还是 XmlDocument?新项目直接用 XDocument
除非你正在维护十年前的 WebForms 项目,或者对接一个只认 XmlNode 的 COM 组件或老版 WCF 配置系统,否则没理由选 XmlDocument。.NET 3.5 之后的所有新代码,XDocument 是默认答案。
原因很实在:XDocument 构造 XML 更像写 XML 本身,查询靠 LINQ 而不是 XPath 字符串,属性和元素值能自动转类型(比如 (int?)elem.Attribute("id")),出错率低得多。
-
XmlDocument创建一个带属性的节点要调CreateElement、SetAttribute、AppendChild三步;XDocument一行搞定:new XElement("user", new XAttribute("id", 123)) -
XmlDocument.SelectNodes("//user")返回XmlNodeList,遍历时容易漏判null;XDocument.Descendants("user")返回IEnumerable<XElement>,空结果就是空集合,不抛NullReferenceException - 命名空间处理上,
XNamespace ns = "http://example.com"; doc.Root.Elements(ns + "item")清晰安全;XmlDocument得配XmlNamespaceManager,漏注册前缀就查不到——这种错误调试起来特别磨人
XDocument.Load() 和 XmlDocument.Load() 加载行为差异
两者都会把整个 XML 加进内存,都不适合处理几十 MB 以上的文件——这点没区别。但加载时的“默认态度”不同,直接影响你后续读取是否踩坑。
-
XDocument.Load(stream)默认忽略空白文本节点(比如换行缩进),Elements()只返回真正有意义的子元素;XmlDocument默认保留所有节点,ChildNodes里可能混着一堆XmlText类型的空白,遍历时得手动过滤NodeType == XmlNodeType.Element - 如果真需要保留空白(比如 XML 是人手编辑的配置模板),
XDocument.Load(stream, LoadOptions.PreserveWhitespace)才生效;XmlDocument没这么直白的开关,得靠PreserveWhitespace = true属性,且必须在Load前设 -
XDocument支持异步加载:await XDocument.LoadAsync(stream, cancellationToken);XmlDocument没原生异步方法,得自己包Task.Run,有线程切换开销
从 XmlDocument 迁移到 XDocument 的三个关键转换点
不是所有旧代码都能一键替换。最常卡住的地方就这三个:
- 查节点:把
xmlDoc.SelectSingleNode("/root/user[@id='1']")换成doc.Root?.Element("user")?.Attribute("id")?.Value == "1"或更稳的doc.Descendants("user").FirstOrDefault(x => (string)x.Attribute("id") == "1") - 改内容:别再用
node.InnerText = "new value";XElement用element.Value = "new value"(覆盖全部子节点)或element.ReplaceNodes("new value")(更精确) - 互转需求:如果第三方库只吃
XmlDocument,用new XmlDocument().Load(xDoc.CreateReader());反过来,XDocument.Load(xmlDoc.CreateNavigator().ReadSubtree())最稳妥(比先Save成字符串再Parse少一次序列化)
大 XML 文件别硬扛——XDocument 也不是万能的
哪怕你选了 XDocument,只要 XML 超过 10–20MB,就该警惕内存和 GC 压力。它和 XmlDocument 一样,是整树加载模型,没有“部分加载”机制。
真遇到设备日志、工业传感器导出的百兆级 XML,就得切回流式处理:XmlReader 是唯一靠谱的选择。这时候 XDocument 的价值不是替代它,而是配合它——比如用 XmlReader 定位到某个 <Workstation> 节点,再用 XElement.ReadFrom(reader) 把那一小段拎出来,交给 LINQ 查询。
这个组合打法容易被忽略:既没丢掉 XDocument 的查询便利,又避开了全量加载的雷。










