[转]C#操作xml文件:DOM与Linq方式对比——创建、读取、查找、增加、删除、修改、替换、移动与复制到其他文件
目录
原文链接:C#操作xml文件:DOM与Linq方式对比——创建、读取、查找、增加、删除、修改、替换、移动与复制到其他文件——CSDN
DOM to XML 与 LINQ to XML#
在C#中,一般用三种方法来读取xml文件:
- XmlDocument(DOM模式)
- 使用 XmlTextReader(流模式)
- 使用 Linq to Xml(Linq模式)
前两种是在程序中访问并操作XML文件:
DOM(文档对象模型):使用DOM的好处在于它允许编辑和更新XML文档,可以随机访问文档中的数据,可以使用XPath查询。但是,DOM的缺点在于它需要一次性的加载整个文档到内存中,对于大型的文档,这会造成资源问题。
流模型:流模型很好地解决了这个问题,因为它对XML文件的访问采用的是流的概念,也就是说,任何时候在内存中只有当前节点,因此内存暂用少。但它是只读的,仅向前的,不能在文档中执行向后导航操作。
Linq模式:利用C#的linq方法(C#的查询功能)来操作xml文件,代码少易于理解。LINQ to XML 使用 .NET 功能,相当于更新的和重新设计的文档对象模型 (DOM) XML 编程接口。
本文主要比较DOM方法与Linq方法:DOM to XML方法中,是从下至上生成 XML 树;即先创建文档,然后创建元素,再将元素添加到文档。这种编码方式不会提供很多有关 XML 树结构的可视信息。LINQ to XML 也支持用此方法创建 XML 树,但也支持另一种方法,即函数构造。 函数构造使用 XElement 和 XAttribute 构造函数生成 XML 树,用于构造 XML 树的代码可显示基础 XML 的结构。
功能对比#
本文使用的xml文件示例:
/*
* <?xml version="1.0" encoding="utf-8"?>
* <school name="new school">
* <class number="01">
* <student id="01" name="Dan" age="18" />
* <student id="02" name="Tom" age="18" />
* </class>
* </school>
*/
命名空间及类#
以下方法,DOM to XML在类XmlDOM中,LINQ to XML在类XmlLinq中。
- DOM to XML
//xml DOM
using System.Xml;
//xml file
using System.IO;
class XmlDOM
{}
- LINQ to XML
//xml linq
using System.Xml.Linq;
//xml file
using System.IO;
class XmlLinq
{}
创建文件#
- DOM to XML
/// <summary>
/// 创建文件
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void CreateXml(string xmlfile)
{
if (!File.Exists(xmlfile))
{
//实例化XmlDocument对象
XmlDocument xd = new XmlDocument();
//xml文件的头文件声明
XmlDeclaration declare = xd.CreateXmlDeclaration("1.0", "utf-8", "yes");
xd.AppendChild(declare);
//创建根节点
XmlElement school = xd.CreateElement("school");
school.SetAttribute("name", "new school");
//创建一级子节点
XmlElement fschool = xd.CreateElement("class");
fschool.SetAttribute("number", "01");
//创建二级第一个子节点
XmlElement fclass = xd.CreateElement("student");
fclass.SetAttribute("id", "01");
fclass.SetAttribute("name", "Dan");
fclass.SetAttribute("age", "18");
//创建二级第二个子节点
XmlElement sclass = xd.CreateElement("student");
sclass.SetAttribute("id", "02");
sclass.SetAttribute("name", "Tom");
sclass.SetAttribute("age", "18");
//添加节点到父节点,添加根节点到对象
xd.AppendChild(school);
school.AppendChild(fschool);
fschool.AppendChild(fclass);
fschool.AppendChild(sclass);
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 创建文件
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void CreateXml(string xmlfile)
{
if (!File.Exists(xmlfile))
{
//方法一 按照DOM方式添加
//实例化XDocument对象
XDocument xdoc = new XDocument();
//创建根节点
XElement root = new XElement("school");
//创建一级子节点
XElement fchild = new XElement("class");
fchild.SetAttributeValue("number", "01");
//创建二级第一个子节点
XElement schild1 = new XElement("student");
schild1.SetAttributeValue("id", "01");
schild1.SetAttributeValue("name", "Dan");
schild1.SetAttributeValue("age", "18");
//创建二级第二个子节点
XElement schild2 = new XElement("student");
schild2.SetAttributeValue("id", "02");
schild2.SetAttributeValue("name", "Tom");
schild2.SetAttributeValue("age", "18");
//添加节点到父节点,添加根节点到对象
fchild.Add(schild1);
fchild.Add(schild2);
root.Add(fchild);
xdoc.Add(root);
//使用XML的保存会自动在xml文件开始添加:<?xml version="1.0" encoding="utf-8"?>
xdoc.Save("school.xml");
//方法二 按照xml文件树状格式添加
XDocument xd = new XDocument(
new XElement("school", new XAttribute("name", "new school"),
new XElement("class", new XAttribute("number", "01"),
new XElement("student", new XAttribute("id", "01"), new XAttribute("name", "Dan"), new XAttribute("age", "18")),
new XElement("student", new XAttribute("id", "02"), new XAttribute("name", "Tom"), new XAttribute("age", "18"))
)
)
);
//使用XML的保存会自动在xml文件开始添加:<?xml version="1.0" encoding="utf-8"?>
xd.Save(xmlfile);
}
}
读取文件#
- LINQ to XML
/// <summary>
/// 读取文件
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void ReadXml(string xmlfile)
{
if (File.Exists(xmlfile))
{
//省略了判定xml文件存在
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
XElement root = xd.Root;
//获取根节点下所有子节点
IEnumerable<XElement> xe = root.Elements();
foreach (XElement fxe in xe)
{
foreach (XElement sxe in fxe.Elements())
{
Console.WriteLine(sxe.Name);
Console.WriteLine(sxe.Attribute("id").Value);
}
}
}
Console.ReadLine();
}
在文件中查找#
- DOM to XML
/// <summary>
/// 在文件中查找
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void SearchInXml(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
//查找固定名称 节点名要从根节点开始写
XmlNode node_name = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
XmlElement ele_name = (XmlElement)node_name;
//查找固定位置
XmlNode node_position = xd.DocumentElement.SelectSingleNode("/school/class/student[position()=2]");
XmlElement ele_position = (XmlElement)node_position;
//保存到其他xml中测试上面的结果
CopyToOtherXmlNode("dom.xml", "dom1.xml", node_name);
CopyToOtherXmlNode("dom.xml", "dom1.xml", ele_name);
CopyToOtherXmlNode("dom.xml", "dom1.xml", node_position);
CopyToOtherXmlNode("dom.xml", "dom1.xml", ele_position);
}
}
- LINQ to XML
/// <summary>
/// 在文件中查找
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void SearchInXml(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
//获取所有节点
IEnumerable<XNode> nodes = xd.DescendantNodes();
//找到单个节点
IEnumerable<XElement> node = xd.Descendants("class");
//找到特点节点 子节点有element且其属性id值为01
IEnumerable<XElement> element = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01");
//如果只取一个,可以用下面这个方法 First表示枚举中的第一个
XElement element_first = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First();
//把结果保存到单独的xml文件以进行观察
XDocument xd_nodes = new XDocument(
new XElement("school",
nodes
)
);
xd_nodes.Save("nodes.xml");
XDocument xd_node = new XDocument(
new XElement("school",
node
)
);
xd_node.Save("node.xml");
XDocument xd_element = new XDocument(
new XElement("school",
element
)
);
xd_element.Save("element.xml");
//获取值
var selement = xd.Descendants("class")
.Select(p => new { age = p.Element("student") }); //获得节点
var welement = xd.Descendants("class")
.Where(p => p.Element("student").Attribute("name").Value.Contains("D")) //条件筛选 寻找student:其name属性的值包含有字母D
.Select(p => new { age = p.Element("student") }); //获得节点
//输出结果
string strs = "";
foreach (var a in selement)
{
strs += a.age + "\r\n";
}
Console.Write(strs);
string strw = "";
foreach (var a in selement)
{
strw += a.age + "\r\n";
}
Console.Write(strw);
Console.Read();
}
}
追加节点、属性和内容#
- DOM to XML
/// <summary>
/// 追加节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void AppendNode(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
XmlNode node = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
//添加节点
XmlElement ele = xd.CreateElement("sport");
ele.InnerText = "football";
//InsertAfter为在node后加,InsertBefore为在node前加,这两种增加后的节点与node同级
//AppendChild为在node的最后一个子节点后再追加一个子节点,这种增加后的节点为node的子节点
node.AppendChild(ele);
//添加属性
XmlElement nodee = (XmlElement)node;
nodee.SetAttribute("gender", "male");
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 追加节点、属性和内容
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void AppendNode(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
XElement parent_class = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First();
XElement ele = new XElement("student", new XAttribute("id", "03"), "Sue");
parent_class.Add(ele);
xd.Save(xmlfile);
}
}
删除节点#
- DOM to XML
/// <summary>
/// 删除节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void DeleteNode(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
XmlNode node = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
xd.DocumentElement.FirstChild.RemoveChild(node);
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 删除节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void DeleteNode(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
//删除节点 全部没有了
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First()
.Remove();
//删除节点内容 只剩下节点名
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First()
.RemoveAll();
//删除节点属性 内容还在
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First()
.RemoveAttributes();
//删除节点内容 只剩下节点名和属性
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First()
.RemoveNodes();
xd.Save(xmlfile);
}
}
修改节点内容和属性#
- DOM to XML
/// <summary>
/// 修改节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void ModifyNode(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
XmlNode node = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
node.InnerText = "node text";
node.Attributes["age"].Value = "19";
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 修改节点内容和属性
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void ModifyNode(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
XElement ele = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First();
ele.Value = "This is a class";
ele.Attribute("number").Value = "1";
xd.Save(xmlfile);
}
}
替换节点或内容#
- DOM to XML
/// <summary>
/// 替换节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void ReplaceNode(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
//如果用同一文件的节点替换,相当于删除被替换节点,将替换节点从原位置移动到被替换节点位置
XmlNode node = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
XmlNode node2 = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='02']");
xd.DocumentElement.FirstChild.ReplaceChild(node2, node);
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 替换节点或内容
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void ReplaceNode(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
//将元素的子元素替换为新的内容 节点名仍保留,其他全部被替换
XElement ele = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").First();
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").Last()
.ReplaceAll(ele);
//将元素属性替换为文本 元素名和子节点保留
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").Last()
.ReplaceAttributes("123");
//将元素子节点替换为新的内容 元素名和属性保留
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").Last()
.ReplaceNodes("123");
//将元素替换为新的内容 全都没有了
xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01").Last()
.ReplaceWith("123");
xd.Save(xmlfile);
}
}
在当前文件中移动或复制节点#
- DOM to XML
/// <summary>
/// 在当前文件中移动或复制节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="nodename">节点属性名称</param>
public void MoveOrCopyNode(string xmlfile, string nodename)
{
if (File.Exists(xmlfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
XmlNode node = xd.DocumentElement.SelectSingleNode("/school/class/student[@id='" + nodename + "']");
////在C#中从一个结构到另一个相同的结构,必须先取出节点然后使用import
////如从一个xml的节点到另一个xml需要用ImportNode
////如从一个databale的行到另一个databale需要用ImportRow
XmlNode markNode = xd.ImportNode(node, true);
//下面两句话可以用上面的ReplaceNode方法
//如果是移动节点则取消下面这句话的注释:删除原节点
//xd.DocumentElement.FirstChild.RemoveChild(node);
xd.DocumentElement.AppendChild(markNode);
xd.Save(xmlfile);
}
}
- LINQ to XML
/// <summary>
/// 在当前文件中移动或复制节点
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
public void MoveOrCopyNode(string xmlfile)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
//找出节点然后添加 如果是移动还要删除原来位置
IEnumerable<XElement> ele = xd.Descendants("class")
.Where(p => (string)p.Element("student").Attribute("id").Value == "01");
ele.ElementAt(0).Add(ele.ElementAt(1));
xd.Save(xmlfile);
}
}
将节点复制到其他xml文件#
- DOM to XML
/// <summary>
/// 将节点复制到其他xml文件
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="desfile">包含路径和扩展名的目标文件名</param>
/// <param name="movenode">要移动的节点</param>
public void CopyToOtherXmlNode(string xmlfile, string desfile, XmlNode movenode)
{
if (File.Exists(xmlfile) && File.Exists(desfile))
{
XmlDocument xd = new XmlDocument();
xd.Load(xmlfile);
XmlDocument xm = new XmlDocument();
xm.Load(desfile);
////在C#中从一个结构到另一个相同的结构,必须先取出节点然后使用import
////如从一个xml的节点到另一个xml需要用ImportNode
////如从一个databale的行到另一个databale需要用ImportRow
XmlNode markNode = xm.ImportNode(movenode, true);
xm.DocumentElement.AppendChild(markNode);
xm.Save(desfile);
}
}
- LINQ to XML
/// <summary>
/// 将节点复制到其他xml文件
/// </summary>
/// <param name="xmlfile">包含路径和扩展名的文件名</param>
/// <param name="xmlfile2">要添加节点的文件的文件名</param>
public void CopyToOtherXmlNode(string xmlfile, string xmlfile2)
{
if (File.Exists(xmlfile))
{
XDocument xd = new XDocument();
xd = XDocument.Load(xmlfile);
IEnumerable<XElement> node = xd.Descendants("class");
//把结果保存到单独的xml文件以进行观察
XDocument xd_node = new XDocument(
new XElement("school",
node
)
);
xd_node.Save("node.xml");
//把结果保存到已存在的xml文件的根结点末尾,作为根结点最后一个子节点
XDocument xd2 = new XDocument();
xd2 = XDocument.Load(xmlfile2);
xd2.Root.Add(node);
}
}
下载完整代码learning-everything/csharp/CsharpFunction/XmlDOM.cs、learning-everything/csharp/CsharpFunction/XmlLinq.cs。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!