XDocument.Load()是最稳妥的XML加载方式,适合完整文档;需注意UTF-8编码、流可重读性、默认命名空间处理及Elements()与Descendants()的区别。

用 XDocument 加载 XML 文件最稳妥
直接用 XDocument.Load() 读取本地文件或流,比 XElement.Load() 更适合带声明、注释、处理指令的完整 XML 文档。它保留根节点上下文,后续查子节点、命名空间更可靠。
- 如果 XML 文件路径含中文或特殊字符,先确保文件编码是 UTF-8(无 BOM);否则可能抛
XmlException: Data at the root level is invalid - 网络响应流(如
HttpWebResponse.GetResponseStream())必须可重读,否则XDocument.Load()可能读空 —— 建议用new StreamReader(stream).ReadToEnd()转成字符串再用XDocument.Parse() - 不推荐用
XmlReader.Create()配合XDocument.Load()做预过滤,除非 XML 超大(百 MB 级),否则徒增复杂度
查询节点时小心默认命名空间
很多真实 XML(如 Office Open XML、SOAP 响应)带 xmlns="http://..."。此时用 doc.Descendants("Item") 查不到任何节点 —— 因为 "Item" 被当作无命名空间名,而实际节点属于默认命名空间。
- 先提取默认命名空间:
XNamespace ns = doc.Root.GetDefaultNamespace(); - 查询时显式拼接:
doc.Descendants(ns + "Item") - 若 XML 有多个前缀(如
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"),用doc.Root.GetNamespaceOfPrefix("xsi")获取对应XNamespace - 忽略命名空间强行查(仅调试用):
doc.Descendants().Where(e => e.Name.LocalName == "Item"),但会丢失命名空间语义,慎用于生产
Elements() 和 Descendants() 别混用
两者行为差异直接影响性能和结果:
-
root.Elements("Child"):只查直接子元素,层级明确,速度快,适合已知结构 -
root.Descendants("Child"):递归查所有后代,可能跨多层,易误匹配(比如嵌套的同名子节点) - 若 XML 深度大、节点多,
Descendants()可能触发全树遍历;高频调用建议先缓存IEnumerable结果,而非反复调用 - 想查“某元素下的第一个
Value”,别写e.Descendants("Value").First(),改用e.Elements("Group").Elements("Value").FirstOrDefault(),更精准也更快
var doc = XDocument.Load("config.xml");
XNamespace ns = doc.Root.GetDefaultNamespace();
// 正确:带命名空间查询
var items = doc.Root.Element(ns + "Data")
.Elements(ns + "Record")
.Select(r => new {
Id = (int)r.Element(ns + "Id"),
Name = (string)r.Element(ns + "Name")
}).ToList();
命名空间处理和层级选择是出错最多的地方,尤其当 XML 来自第三方服务时,务必先用 doc.ToString() 打印结构确认根节点声明和实际元素路径。










