在C#的世界里操作XML,老一辈开发者最先想到的往往是System.Xml命名空间下的XmlDocument。它基于W3C的DOM模型,功能完整但用起来偏繁琐。自从.NET 3.5引入LINQ to XML之后,事情变得简单多了。System.Xml.Linq命名空间提供了一套全新的API,核心类包括XDocument、XElement、XAttribute等,它天生与LINQ查询语法融合,可以用非常自然的方式查询、构建和修改XML。本文将系统讲解LINQ to XML的用法,并与XmlDocument做一个全面的对比。

一、LINQ to XML 的核心类与基本用法
LINQ to XML的类层次结构设计得很清晰。XDocument代表整个XML文档,XElement代表一个元素节点,XAttribute代表属性,XText代表文本内容。其中最常用的是XElement,事实上很多场景下你甚至不需要XDocument,直接操作XElement就够了。
先看加载XML的几种方式。XDocument.Load可以从文件路径、URL或流中加载,XDocument.Parse则用于解析XML字符串:
// 从文件加载
XDocument doc = XDocument.Load(@"C:\data\books.xml");
// 从字符串解析
XDocument doc2 = XDocument.Parse("<books><book id=\"1\"><title>C#入门</title></book></books>");
// 读取元素值
string title = doc2.Root.Element("book").Element("title").Value;
Console.WriteLine(title); // 输出:C#入门
// 读取属性值
string id = doc2.Root.Element("book").Attribute("id").Value;
这段代码里用到了Element和Attribute两个方法。它们只返回第一个匹配的子元素或属性,如果需要获取所有匹配项,用Elements和Attributes(返回集合)。另外还有一个非常实用的Descendants方法,它可以递归查找所有后代元素,不管嵌套多少层都能一网打尽,这在查找深层节点时特别方便。
保存文档同样简单,调用Save方法即可,支持文件路径、流和XmlWriter。LINQ to XML默认生成的XML格式是缩进良好的,不需要额外配置格式化选项。
二、用LINQ查询语法操作XML
这是LINQ to XML最大的卖点。它把XML的子元素和属性做成了IEnumerable<T>集合,因此可以直接用LINQ的查询表达式或方法语法进行筛选、排序、投影。假设有一个图书列表,我们想找出价格高于50元的书并按价格排序:
XDocument doc = XDocument.Load("books.xml");
// 查询语法
var query = from book in doc.Root.Elements("book")
where (decimal)book.Element("price") > 50
orderby (decimal)book.Element("price") descending
select new
{
Id = (string)book.Attribute("id"),
Title = (string)book.Element("title"),
Price = (decimal)book.Element("price")
};
foreach (var item in query)
{
Console.WriteLine($"{item.Id} - {item.Title}: {item.Price}");
}
// 等价的方法语法
var query2 = doc.Descendants("book")
.Where(b => (decimal)b.Element("price") > 50)
.OrderByDescending(b => (decimal)b.Element("price"));
注意代码里的类型转换写法,比如(decimal)book.Element("price")。LINQ to XML为XElement和XAttribute定义了一整套显式转换运算符,可以直接转成string、int、decimal、DateTime等常用类型。这比XmlDocument里的InnerText加int.Parse的组合要优雅得多,而且转换运算符内部对null做了处理,节点不存在时转换结果是null而不是抛异常,代码更健壮。
查询还支持命名空间的处理。通过XNamespace类可以构造命名空间,然后用加号运算符拼接元素名,例如ns + "book",这比XmlDocument里拼接{namespace}element字符串的写法直观不少。
三、函数式构造:一行代码创建XML树
创建XML是LINQ to XML另一个亮点,官方称之为函数式构造(Functional Construction)。它的思路是把整棵XML树用一个构造表达式写出来,结构与最终XML完全一致,一眼就能看明白:
XElement xml = new XElement("books",
new XElement("book",
new XAttribute("id", "1"),
new XElement("title", "C#高级编程"),
new XElement("price", 89.00m)
),
new XElement("book",
new XAttribute("id", "2"),
new XElement("title", "LINQ实战"),
new XElement("price", 65.50m)
)
);
Console.WriteLine(xml.ToString());
输出的XML与代码结构完全对应,缩进自动完成。如果用XmlDocument写同样的内容,需要反复调用CreateElement、CreateAttribute、AppendChild、SetAttributeNode等方法,代码长度往往是几倍,而且结构被淹没在方法调用里,可读性差很远。
修改XML同样直接。给XElement调用SetElementValue或SetAttributeValue即可更新或创建节点,删除节点用Remove,替换用ReplaceWith。还可以配合LINQ做批量操作,比如把所有价格低于30元的书整体删掉:
xml.Descendants("book")
.Where(b => (decimal)b.Element("price") < 30m)
.ToList()
.ForEach(b => b.Remove());
这里要注意一个坑:在枚举Descendants的同时直接删除节点会导致集合被修改而抛异常,所以必须先ToList物化结果再删除。这是实际开发中非常常见的错误。
四、与XmlDocument 的全面对比
两者最根本的区别在于设计理念。XmlDocument实现的是标准DOM,API是命令式的、以文档为中心;LINQ to XML是专门为.NET设计的,API是函数式的、以查询为中心。下面从几个具体维度展开对比。
第一是代码量与可读性。前面的例子已经说明,无论是查询还是构造,LINQ to XML的代码都明显更短,结构更贴近XML本身。XmlDocument的代码充斥着工厂方法调用,维护成本高。
第二是查询能力。XmlDocument只支持XPath,XPath本身功能不弱,但它是字符串表达式,编译期无法检查,写错了只有运行时才发现。LINQ查询是强类型的,智能提示、编译检查、重构支持全部到位。
第三是性能。在单纯加载和遍历的场景下,LINQ to XML的XDocument通常比XmlDocument略快,内存占用也更小,因为它内部用的是轻量级的链接列表而非通用DOM。但如果需要频繁地随机访问和修改同一棵树,两者差距不大。真正追求极致性能时应考虑XmlReader和XmlWriter这种流式API,它们不把整棵树加载进内存。
第四是互操作性。有一个场景XmlDocument仍然占优:它与旧系统、旧API(比如返回XmlNode的接口)以及某些第三方库的兼容性更好。另外如果代码里大量使用现成的XPath表达式,迁移也需要成本。这时可以在两者之间转换,XDocument也提供了CreateReader和CreateWriter方法与XmlReader、XmlWriter互通。
五、选型建议与常见注意事项
结论很明确:新项目无脑选LINQ to XML,除非被迫与只支持DOM的旧接口对接。它代码简洁、类型安全、与C#语言特性深度集成,是微软官方推荐的主流方案。
使用时有几点需要注意。其一,XDocument.Load接收文件路径,XDocument.Parse接收字符串,别用混了。其二,访问不存在的节点时,Element返回null,继续链式调用会抛NullReferenceException,可以用Elements("x").FirstOrDefault()或显式转换的null容忍特性来规避。其三,处理超大XML文件时,考虑用XNode.ReadFrom配合XmlReader做流式处理,逐节点读入XElement再查询,兼顾内存和便利性。掌握这些细节,LINQ to XML会成为你处理XML最顺手的工具。
LINQ to XMLXmlDocumentC# XML解析修改时间:2026-09-08 09:19:14