使用XDocument读取带命名空间XML需显式注册XmlNamespaceManager;.NET 6+中SelectNodes已过时,应改用XPathNavigator或Descendants+LINQ;访问属性须用?.Value ?? 默认值防空引用。

用 XDocument + XPathSelectElement 读取带命名空间的 XML 属性会失败
直接调用 XElement.XPathSelectElement("//node/@attr") 在多数情况下能工作,但只要 XML 声明了命名空间(比如 xmlns="http://example.com/ns"),默认就查不到任何东西——XPath 查询器不会自动绑定前缀,也不会把无前缀的查询当作默认命名空间处理。
解决办法是显式注册命名空间管理器:
XDocument doc = XDocument.Load("data.xml");
XNamespace ns = "http://example.com/ns";
var manager = new XmlNamespaceManager(new NameTable());
manager.AddNamespace("x", ns.NamespaceName);
var element = doc.XPathSelectElement("/x:root/x:item", manager);
string value = element?.Attribute("id")?.Value;
- 必须用
XmlNamespaceManager注册前缀(哪怕只是临时起个"x") -
XNamespace对象不能直接传给 XPath 方法,必须通过manager - 如果 XML 有多个命名空间,每个都要
AddNamespace,且 XPath 中必须使用对应前缀
SelectNodes 和 SelectSingleNode 在 .NET 6+ 已被标记为过时
很多老教程还在用 XmlDocument.SelectNodes("//book[@category='fiction']"),但在 .NET 6 及以后,这些方法已标为 [Obsolete],编译会警告,且底层依赖已逐步移除。继续用可能在后续版本彻底失效。
替代路径只有两条:
- 坚持用 DOM 模型:改用
XDocument+Descendants()+Where()组合(适合简单条件) - 需要 XPath 表达能力:用
XDocument.CreateNavigator()获取XPathNavigator,再调用其Select()或SelectSingleNode()
例如按属性筛选节点:
XDocument doc = XDocument.Load("books.xml");
XPathNavigator nav = doc.CreateNavigator();
XPathNodeIterator iter = nav.Select("//book[@category='fiction']");
while (iter.MoveNext())
{
string title = iter.Current.SelectSingleNode("title")?.Value;
string isbn = iter.Current.GetAttribute("isbn", "");
}
注意:XPathNavigator 不支持 LINQ 链式调用,也不返回 XElement,需手动提取值。
用 Descendants().FirstOrDefault() 替代 XPath 查询更安全、更易调试
当查询逻辑不复杂(比如“找第一个 config 下的 timeout 属性”),硬写 XPath 反而容易出错:斜杠漏写、大小写不一致、属性名拼错,错误信息又只报“未找到节点”,没上下文。
用 LINQ to XML 更直白:
XDocument doc = XDocument.Load("app.config");
string timeout = doc
.Descendants("config")
.Elements("connection")
.Attributes("timeout")
.FirstOrDefault()?.Value;
- 每一步都可单独打断点验证是否为空
- 编译器能检查元素名、属性名是否存在(拼错会直接报错)
- 不需要处理命名空间前缀或
XmlNamespaceManager - 性能差异在千级节点内几乎不可测
仅当需要通配符(//)、轴步(following-sibling::)或函数(contains(@class, 'active'))时,才值得切回 XPath。
读取空属性或不存在属性时,别直接调 .Value
像 element.Attribute("retryCount").Value 这种写法,一旦属性缺失,会抛 NullReferenceException,而不是返回 null 或空字符串。
正确姿势只有两种:
- 用空合并操作符:
element.Attribute("retryCount")?.Value ?? "0" - 用
Attribute().Value的安全封装(.NET 6+ 推荐):element.Attribute("retryCount")?.Value本身已是可空引用,配合??最简洁
不要写 element.Attribute("retryCount").ToString()——ToString() 对 null 返回空字符串,掩盖了属性缺失的事实,后期排查配置遗漏会非常麻烦。


















