用 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<xelement></xelement>结果,而非反复调用 想查“某元素下的第一个
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()打印结构确认根节点声明和实际元素路径。
