用XDocument.Descendants()筛选最稳,它不依赖层级、容错性强;查节点需处理命名空间、用Where()精准过滤、强制转换取值、性能敏感时缓存结果、判空用Any()。
用
加
做基础筛选最稳
直接加载 XML 后用
查找节点,比
更容错——它不依赖层级深度,也不会因命名空间或空格文本节点崩掉。新手常误用
写成
,结果 XML 里
前有注释或换行就返回空集合。
实操建议:
始终从
或
开始,别用过时的
查任意层级的
节点,写
,不是
(
可能为
)
如果 XML 带默认命名空间(比如
),必须先声明
,否则所有字符串匹配都失效
带条件的查询别硬拼字符串,用
+
或
想查
?别写
—— 这会把整个 XML 序列化再搜,慢、不准、还可能误匹配子节点内容。
正确做法是定位到属性或子元素后取值比较:
查属性:
查子元素文本:
注意强制转换:用
安全取文本(空元素/缺失元素返回
),用
/
转数值(失败抛异常,需确保格式可靠)
性能敏感时避免重复遍历,用
或
缓存结果
返回的是延迟执行的
,每次 foreach 或 Count() 都会重新解析 XML 树。如果后续要多次访问(比如先筛出订单,再分别取客户名、商品列表、总价),不缓存就会反复解析同一段 XML,CPU 和内存开销明显上升。
该缓存就缓存:
确定数据量不大(几千节点以内)且后续要多次读取时,加
一次求值
如果只遍历一次,保持延迟执行反而更省内存
别用
判空,改用
—— 前者强制走完全部节点,后者找到第一个就停
XML 命名空间不处理,
永远查不到
这是最隐蔽也最高频的失败原因。只要 XML 文件顶部有
这类声明,所有未带前缀的元素实际都属于那个命名空间。此时
返回空,不是语法错,是“名字对不上”。
解决方法只有一步:声明命名空间并参与查询:
然后写
或
如果 XML 用了前缀(如
),也要用
,不能直接用
没声明命名空间却写了
会编译失败;声明了但漏加
,运行时永远查不到——这点连很多老手都会忘。
XDocumentDescendants()Descendants()Elements()Elements()doc.Root.Elements("Item")ItemXDocument.Load()XDocument.Parse()XmlDocumentProductdoc.Descendants("Product")doc.Root.Descendants("Product")Rootnullxmlns="http://example.com/ns"XNamespaceWhere()Attribute()Element().Where(x => x.ToString().Contains("shipped"))doc.Descendants("Order").Where(o => (string)o.Attribute("Status") == "shipped")doc.Descendants("Product").Where(p => (string)p.Element("Price") != null && (decimal)p.Element("Price") > 100)(string)null(int)(decimal)ToList()ToArray()Descendants()IEnumerable.ToList().Count() > 0.Any()Descendants("Tag")xmlns="http://tempuri.org/"doc.Descendants("User")XNamespace ns = "http://tempuri.org/";doc.Descendants(ns + "User")doc.Root?.Descendants(ns + "Order")xmlns:ns="http://example.com"ns + "Tag""ns:Tag"ns + "Tag"ns + 