新项目优先用xdocument,因其基于linq to xml、语法简洁、内存占用低、空安全;xmldocument易出节点上下文错误、操作繁琐、路径和命名空间处理复杂。

用 XDocument 还是 XmlDocument?选错直接影响可维护性
新项目优先用 XDocument,它基于 LINQ to XML,写法更直观、内存占用更低、支持函数式操作;XmlDocument 是老式 DOM 模型,节点操作繁琐,容易漏调 AppendChild 或忘 ImportNode。
常见错误现象:XmlDocument 下直接用 CloneNode(true) 复制节点后插入到另一个文档,结果抛出 "The node to be inserted is from a different document context" —— 因为没调 ImportNode。
- 新增节点时,
XDocument直接用new XElement("name", "value")构造,链式添加即可 -
XmlDocument新增元素必须分三步:创建元素 → 设置属性/文本 → 显式追加到父节点 - 读取单个值,
XDocument推荐用Element("xxx")?.Value,空安全;XmlDocument得先判!= null再取FirstChild?.Value
增删改查时路径写错,90% 的异常都发生在这里
XML 路径不是文件路径,大小写敏感、不自动忽略空白、不隐式匹配命名空间 —— 写错一个字母或少一个斜杠就返回 null 或空集合。
典型错误:doc.Root.Element("users").Elements("user") 报 NullReferenceException,其实是因为根节点名是 Users(首字母大写),或者中间有默认命名空间但没声明。
- 查节点前先确认根名:
doc.Root?.Name,别凭印象硬写 - 含命名空间的 XML 必须用
XNamespace前缀,比如var ns = "http://tempuri.org"; doc.Root?.Element(ns + "item") - 用
Descendants("xxx")替代深层嵌套的Element().Element().Element(),更鲁棒(但性能略低) - 删除节点别只调
Remove(),得确保它还在父节点里 —— 先parent?.RemoveNodes()或用parent?.Elements("xxx").Remove()
保存文件时中文乱码、格式错乱、覆盖失败
默认 Save() 用 UTF-8 无 BOM,但 Windows 记事本打开会当 ANSI;缩进失效常因用了 Load() 但没设 LoadOptions.PreserveWhitespace;覆盖失败多是文件被其他进程(如记事本、VS)独占锁定。
- 显式指定编码:用
doc.Save(new StreamWriter("a.xml", false, Encoding.UTF8)),避免系统区域设置干扰 - 保留缩进:创建
XDocument时传new XmlWriterSettings { Indent = true, IndentChars = " " },再用doc.Save(XmlWriter.Create(...)) - 避免覆盖冲突:保存前加
File.Copy("a.xml", "a.xml.bak", true)备份;或用try/catch (IOException)提示“文件正被占用” - 不要用
File.WriteAllText直接写字符串 —— 会丢失格式、转义错误、破坏 XML 结构
查不到数据?先看是不是把 XML 当成 JSON 在用
XML 没有“数组”概念,同名子节点天然就是集合;也没有“扁平键路径”,user/name 和 user/address/city 是两层完全独立的结构。习惯 JSON 思维的人常误以为 Element("user").Element("name") 能跨层级跳转。
- 查多个同级
user:用doc.Root?.Elements("user"),不是doc.Root?.Element("user")(后者只取第一个) - 查带属性的节点:
doc.Root?.Elements("item").FirstOrDefault(x => x.Attribute("id")?.Value == "123"),别漏?.Value - 修改文本内容:直接赋值
elem.Value = "new text",别用elem.Add(new XText("...")),否则会拼接而非替换 - 属性不存在时
Attribute("xxx")返回null,不能直接 .Value —— 用Attribute("xxx")?.Value或Attribute("xxx")?.Value ?? "default"
最易被忽略的是命名空间和大小写,这两个点不验证,后面所有操作都是空中楼阁。










