C#玩转XML:从配置文件到数据交换,节点属性的增删改查一篇通
你是不是也曾经对着一个复杂的XML配置文件手足无措,或是需要从一堆嵌套的节点里挖出某个数据时感到头皮发麻?
先看案例:一位开发者,为了手动修改一个软件生成的、包含几百个节点的XML报告,硬是加班到凌晨两点,用最笨的“查找-替换”大法,结果还因为一个标签没闭合导致整个文件解析失败。这种经历,在开发圈里,真不算少见。
XML,作为曾经的数据交换和配置存储的“老将”,至今仍在许多系统(如App.config、Web服务参数、Office文档结构)中扮演核心角色。掌握C#操作XML的精髓,就是掌握了一把打开这些系统的万能钥匙。今天,把增、删、改、查这四招,讲透、练熟。
🎯 核心摘要:你能从本文得到什么
1. 理解XML文档在C#中的两种主流操作模型。 2. 掌握使用XmlDocument和XDocument进行节点与属性增删改查的实战代码。 3. 避开常见陷阱(如编码错误、性能问题)。 4. 获得一套可直接复制使用的代码模板。
📖 主要内容脉络
🔹 第一部分:XML与C#——为何而战?
🔹 第二部分:两大核心武器库——XmlDocument vs. XDocument
🔹 第三部分:实战演练——四招吃遍天(含完整代码)
🔹 第四部分:避坑指南与进阶思考
🔍 第一部分:XML与C#——为何而战?
你可以把XML文档想象成一棵“家族树”。
— 树干是根节点(Root)。
— 树枝是元素节点(Element)。
— 树叶可以是文本,也可以是挂在树枝上的小卡片(属性,Attribute)。
— 整个家族还有家规(声明,Declaration)和旁白注释(Comment)。
C#要做的,就是帮我们程序化地绘制、修改、查询这棵家族树。最常见的两个场景是:读取应用的配置信息、生成或解析与其他系统交换的数据包。
在.NET世界里,我们主要倚重两个“家族树管理员”:
1️⃣ System.Xml 命名空间下的 XmlDocument (DOM模型)。年纪稍大,但稳重权威,将整棵树一次性加载到内存,方便随意访问任何部位。
2️⃣ System.Xml.Linq 命名空间下的 XDocument (LINQ to XML)。后起之秀,更加灵活轻便,语法优雅,配合LINQ查询如虎添翼。
本文将同时讲解这两位,你可以按喜好和项目需求选择。
⚙️ 第二部分:两大核心武器库——XmlDocument vs. XDocument
在深入代码前,快速了解下这两位“管理员”的脾气:
🏛️ XmlDocument (经典DOM)
— 特点:基于W3C DOM标准,方法名偏长(如AppendChild, CreateElement)。
— 适用:需要严格DOM兼容性,或处理非常复杂的XPath查询。
— 关键对象:XmlDocument, XmlElement, XmlAttribute。
🚀 XDocument (LINQ to XML)
— 特点:API设计更符合C#习惯,声明式创建,与LINQ无缝集成。
— 适用:绝大多数新项目推荐使用,代码更简洁易读。
— 关键对象:XDocument, XElement, XAttribute。
重要提示: 对于新项目,除非有特殊要求,否则建议优先选择XDocument,它的学习曲线更平缓,代码也更漂亮。
💻 第三部分:实战演练——四招吃遍天
假设我们要管理一个“书籍仓库”的XML文件。
📖 第一招:创建与加载
目标: 从零创建或从现有文件加载XML文档。
1. 使用 XmlDocument:
// 创建新文档
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xmlDoc.AppendChild(xmlDecl);
// 创建根节点并添加
XmlElement root = xmlDoc.CreateElement("BookStore");
xmlDoc.AppendChild(root);
// 保存到文件
xmlDoc.Save("books.xml");
// 从文件加载
XmlDocument loadDoc = new XmlDocument();
loadDoc.Load("books.xml"); // 或者使用 LoadXml 从字符串加载
2. 使用 XDocument (更简洁):
// 声明式创建新文档
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("BookStore")
);
// 保存
xDoc.Save("books_linq.xml");
// 从文件加载
XDocument loadXDoc = XDocument.Load("books_linq.xml");
🔎 第二招:查询(查)
目标: 找出所有“科幻”类别的书,并获取其标题。
1. 使用 XmlDocument (配合 XPath,功能强大):
XmlDocument doc = new XmlDocument();
doc.Load("books.xml");
// 使用XPath查询
XmlNodeList scifiBooks = doc.SelectNodes("/BookStore/Book[@category='Sci-Fi']");
foreach (XmlNode book in scifiBooks)
{
string title = book.SelectSingleNode("Title").InnerText;
Console.WriteLine($"找到科幻书: {title}");
}
2. 使用 XDocument (配合LINQ,直观优雅):
XDocument xDoc = XDocument.Load("books_linq.xml");
var scifiBooks = from book in xDoc.Descendants("Book")
where (string)book.Attribute("category") == "Sci-Fi"
select new {
Title = book.Element("Title").Value,
Price = (decimal)book.Element("Price")
};
foreach (var book in scifiBooks)
{
Console.WriteLine($"LINQ找到: {book.Title}, 价格: {book.Price}");
}
✏️ 第三招:修改与新增(增、改)
目标1: 为第一本书添加一个“折扣”属性,并修改其价格。
// 使用 XDocument 示例
XElement firstBook = xDoc.Descendants("Book").FirstOrDefault();
if (firstBook != null)
{
// 添加或修改属性
firstBook.SetAttributeValue("discount", "0.8"); // 增/改属性
// 修改元素值
firstBook.Element("Price").Value = (decimal.Parse(firstBook.Element("Price").Value) * 0.8m).ToString();
// 新增一个子元素
firstBook.Add(new XElement("Isbn", "978-1234567890"));
}
xDoc.Save("books_updated.xml");
目标2: 在根节点下新增一本书。
xDoc.Root.Add(
new XElement("Book",
new XAttribute("category", "History"),
new XElement("Title", "明朝那些事儿"),
new XElement("Author", "当年明月"),
new XElement("Price", "49.90")
)
);
xDoc.Save("books_updated.xml");
🗑️ 第四招:删除
目标: 删除所有价格超过100元的书。
// 使用 XDocument
var expensiveBooks = xDoc.Descendants("Book")
.Where(b => (decimal)b.Element("Price") > 100)
.ToList(); // 先ToList避免遍历时修改集合的错误
foreach (var book in expensiveBooks)
{
book.Remove(); // 删除节点及其所有子内容
}
// 如果只想删除某个属性
XElement aBook = xDoc.Descendants("Book").First();
aBook.Attribute("discount")?.Remove(); // 安全地移除属性
xDoc.Save("books_pruned.xml");
⚠️ 第四部分:注意事项与进阶思考
1. 编码问题: 保存时确保指定正确的编码(如UTF-8),否则中文等字符可能出现乱码。使用XDeclaration或XmlWriterSettings明确指定。
2. 性能考量: XmlDocument会一次性将整个文档加载进内存,处理超大XML文件(几百MB以上)时可能内存不足。考虑使用XmlReader进行只读、向前的流式读取,或用XmlWriter流式写入。
3. 线程安全: XmlDocument和XDocument都不是线程安全的。在多线程环境下操作同一文档实例,需要自己加锁。
4. 命名空间: 如果XML带有命名空间(如SOAP消息),查询时必须带上,否则会找不到节点。处理起来稍复杂,记住在XPath或LINQ中正确包含命名空间URI。
5. 永远不要用字符串拼接生成XML! 这极易产生格式错误或XML注入漏洞。务必使用上述API来创建节点和属性。
进阶方向: 当你熟练掌握基础操作后,可以探索XPath高级查询语法、使用XmlSerializer将XML与对象直接互转、或者学习XSD来验证XML结构的正确性。
XML操作就像编程世界里的“基本功”,看起来简单,但细节决定成败。希望这篇融合了两种主流方法的指南,能成为你案头随时可查的利器。
光看是学不会的,最好的方式就是打开Visual Studio,把上面的代码敲一遍,然后尝试修改、扩展,甚至故意写错看看会报什么错。过程中有任何疑问,或者你发现了更巧妙的心得,都欢迎来聊聊。
如果觉得这篇总结对你有帮助,不妨点个赞或收藏。技术路上坑很多,关注我,咱们一起填坑,分享更多这样即学即用的实战经验。下次见!
浙公网安备 33010602011771号