博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#学习之Linq to Xml
阅读量:6756 次
发布时间:2019-06-26

本文共 15788 字,大约阅读时间需要 52 分钟。

前言

我相信很多从事.NET开发的,在.NET 3.5之前操作XML会比较麻烦,但是在此之后出现了Linq to Xml,而今天的主人公就是Linq to Xml,废话不多说,直接进入主题。

题外:最近由于身体原因去医院,耽误了不少时间,不然这篇随笔可能早就完成了。

 

 

目录: 

 

一、生成Xml

为了能够在结构有一定的组织,笔者建议大家新建一个控制台项目,并且新建一个CreateXml(以下部分都属于该类中)

并在其中写入以下属性:

1         public static String Path2         {3             get4             {5                 String path = String.Format("{0}\\test.xml", Environment.CurrentDirectory);6                 return path;7             }8         }

 

 

这句代码很好理解,就是为了下面我们示例的时候可以将xml保存到当前程序的运行路径下。

以下的示例中不会包含Main方法中的写法,因为Main中仅仅只要调用该静态方法即可。

 

1.创建简单的Xml

首先我们先练练手,创建一个简单的Xml并保存到一个文件中。

代码如下:

1 ///  2 /// 创建简单的xml并保存 3 ///  4 public static void CreateElement() 5 { 6 XDocument xdoc = new XDocument( 7 new XDeclaration("1.0", "utf-8", "yes"), 8 new XElement("root", 9 new XElement("item", "1"),10 new XElement("item", "2")11 ));12 xdoc.Save(Path);13 }

 

 

很多学习过XML的人可以从结构就能够猜测出最终的xml的组织,而这也是linq to xml的优点之一。这句代码首先创建一个xml文档,并设置该xml的版本为1.0,

采用utf-8编码,后面的yes表示该xml是独立的。下面就开始创建每个节点的,首先是Root节点,然后在Root节点中添加两个Item节点。

 

最终生成的Xml如下所示:

1 
2
3
1
4
2
5

 

 

2.创建注释

xml有很多项时,我们就需要利用注释加以区别,通过linq to xml我们一样可以在其中添加注释。

 

比如下面这段代码:

1         ///  2         /// 创建注释 3         ///  4         public static void CreateComment() 5         { 6             XDocument doc = new XDocument( 7                 new XDeclaration("1.0", "utf-8", "yes"), 8                 new XComment("提示"), 9                 new XElement("item", "asd")10                 );11             doc.Save(Path);12         }

 

 

这里我们直接在版本信息的后面添加了一条注释。

 

最终的结果如下所示:

1 
2
3
asd

 

 

3.根据对象创建xml

很多时候我们都会将数组之类的类型转换成xml以便保存进永久性存储介质中,所以下面我们也简单的举了一个例子,将数组转换成xml

 

代码如下所示:

1         ///  2         /// 根据对象创建xml并保存 3         ///  4         public static void CreateElementByObjects() 5         { 6             var s = Enumerable.Range(1, 10); 7             XElement xele = new XElement( 8                 "Root", 9                 from item in s10                 select new XElement("item", item.ToString())11                 );12             xele.Save(Path);13         }

 

 

一开始的代码 var s = Enumerable.Radge(1,10)是从1开始递增,生成含有10项的数组,以便后面我们进行添加,有了这个数组之后,

我们通过简单的linq语句将数组转换成xml,添加到Root中。

 

保存之后的结果如下:

1 
2
3
1
4
2
5
3
6
4
7
5
8
6
9
7
10
8
11
9
12
10
13

 

 

4.创建属性

有时我们不想创建新的子项去保存数据,而是使用属性的方式去保存。理所应当,linq to xml一样也支持这个功能,下面我们可以通过简单的语句去实现它。

 

代码如下所示:

1         ///  2         /// 创建属性 3         ///  4         public static void CreteAttribute() 5         { 6             XAttribute xa = new XAttribute("V2", "2"); 7             XElement xele = new XElement( 8                 "Root", 9                 new XElement("Item",10                     new XAttribute("V1", "1"),11                     xa12                     ));13             xele.Save(Path);14         }

 

 

我们依然可以看到熟悉的语法,这里我们利用了XAttribute去创建一个属性,并添加到XElement中。

 

最终的结果如下:

1 
2
3
4

 

 

5.创建命名空间

对于一些企业级的xml格式,会非常的严格。特别是在同一个xml中可能会出现重复的项,但是我们又想区分开来,这个时候我们可以利用命名空间将他们分开(跟C#中的命名空间类似。)。

 

下面是创建命名空间的示例:

1         ///  2         /// 创建命名空间 3         ///  4         public static void CreateNamespace() 5         { 6             XElement xele = new XElement("{http://www.xamarin-cn.com}Root", 7                 new XElement("Item", "1"), 8                 new XElement("{http://www.baidu.com}Item", 2)); 9             xele.Save(Path);10         }

 

 

结果如下所示:

1 
2
3
1
4
2
5

 

 

从这个结果中我们可以看到对应的属性中有了xmlns属性,并且值就是我们赋给它的命名空间。

 

二、查询并修改Xml

Linq to xml不仅仅是创建xml简单,在查询,编辑和删除方面一样是非常方便的。下面我们就会介绍这些。

首先我们创建一个QueryXml类,并在其中写入如下的属性

1         public static String Path2         {3             get4             {5                 String path = String.Format("{0}\\test1.xml", Environment.CurrentDirectory);6                 return path;7             }8         }

 

 

同时在该路径下新建一个test1.xml文件,并在其中写入如下内容

1 
2
3
Item1
4
Item2
5

 

下面我们就可以正式开始了。

 

1.通过文件读取xml

既然我们要对xml查询就需要读取对应的xml文件,当然后面会介绍其他的方式。

 

代码如下:

1         ///  2         /// 通过文件读取xml 3         ///  4         public static void QueryElementByFile() 5         { 6             XElement xele = XElement.Load(Path); 7             XElement xele1 = xele.Element("Item"); 8             Console.Write(xele1.Value.Trim()); 9             Console.ReadKey();10         }

 

 

我们可以利用XElement的静态方法Load读取指定路径下的xml文件,这里我们不仅读取了该xml文件,同时还获取的该xml的第一个item的值并输出。

 

所以我们可以看到如下的结果:

 

2.在指定节点前后添加新节点

上面我们仅仅只是读取xml以及简单的查询,下面我们不仅仅查询并且还要在该节点前后插入新的节点。

 

代码如下:

1         ///  2         /// 在指定节点前后添加新节点 3         ///  4         public static void AddToElementAfterAndBefore() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Elements("Item") 8                         where ele.Value.Equals("Item2") 9                         select ele).SingleOrDefault();10             if (item != null)11             {12                 XElement nele = new XElement("NItem", "NItem");13                 XElement nele2 = new XElement("BItem", "BItem");14                 item.AddAfterSelf(nele);15                 item.AddBeforeSelf(nele2);16                 xele.Save(Path);17             }18         }

 

 

我们简单的分析一下上面的代码,首先我们利用linq从中查询Item的值为Item2的节点,然后获取其中第一个节点,然后通过AddAfterSelfAddBeforeSelf在该节点的后面和前面分别添加新的节点。

 

添加完之后的xml结果如下:

1 
2
3
Item1
4
BItem
5
Item2
6
NItem
7

 

 

3.添加属性到节点中

我们已经可以动态的添加节点,但是创建的时候不仅仅可以创建节点,并且还能创建属性,下面我们可以通过SetAttributeValue去添加新的属性或者修改现有属性。

 

代码如下:

1         ///  2         /// 添加属性到节点中 3         ///  4         public static void AddAttributeToEle() 5         { 6             XElement xele = XElement.Parse(@"
7
Item1
Item2
"); 8 var item = (from ele in xele.Elements("Item") 9 where ele.Value.Equals("Item2")10 select ele).SingleOrDefault();11 item.SetAttributeValue("v3", "3");12 xele.Save(Path);13 }

 

 

我们可以明显的看出,这里我们已经不是使用XElement.Load去读取xml文件,而是通过直接读取xml字符串。接着我们还是跟上面一样去查询,然后通过SetAttributeValue添加了新的属性,并保存。

 

Xml内容如下:

1 
2
3
4
Item1
5
6
Item2
7

 

 

我们可以看到第二个Item中多了一个 v3=”3” 新的属性。

 

4.添加注释到指定节点前后

这里的语法基本跟添加节点到指定节点前后是相似的,只是读取xml的方式不同。

 

代码如下:

1         ///  2         /// 添加注释到节点前后 3         ///  4         public static void AddCommentToAfterAndBefore() 5         { 6             TextReader tr = new StringReader(@"
7
Item1
Item2
"); 8 XElement xele = XElement.Load(tr); 9 var item = (from ele in xele.Elements("Item")10 where ele.Value.Equals("Item1")11 select ele).FirstOrDefault();12 if (item != null)13 {14 XComment xcom = new XComment("后面的注释");15 XComment xcoma = new XComment("前面的注释");16 item.AddAfterSelf(xcom);17 item.AddBeforeSelf(xcoma);18 }19 tr.Close();20 xele.Save(Path);21 }

 

 

上面我使用StringReaderTextReader读取xml字符串并使用XElement.Load读取该对象,然后就是在新建节点的时候新建的是注释节点,最后利用一样的语法添加到指定节点前后。

 

最终结果如下:

1 
2
3
4
5
Item1
6
7
8
Item2
9

 

 

5.替换指定节点

修改节点的值通过SetValue即可做到,但是有时涉及到子节点,而我们想一次性全部替换掉,那么我们就需要使用ReplaceWith

 

代码如下:

1         ///  2         /// 替换指定节点 3         ///  4         public static void ReplaceElement() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Elements("Item") 8                         where ele.Value.Equals("Item2") 9                         select ele).FirstOrDefault();10             if (item != null)11             {12                 item.ReplaceWith(new XElement("Item", "Item3"));13             }14             xele.Save(Path);15         }

 

 

这里的重点在于ReplaceWith方法,调用该方法会发生两个操作。首先是删除该节点,然后在该节点的位置上将我们的节点插入完成替换。

 

最后的xml结果如下:

1 
2
3
4
5
Item1
6
7
8
Item3
9

 

 

这样我们很轻易的就替换了整个节点。

 

6.删除指定属性

前面我们介绍了创建、修改和添加属性,但是还没有介绍如何删除指定的属性,下面我们就通过一个简单的实例来演示。

 

代码如下:

1         ///  2         /// 删除指定属性 3         ///  4         public static void RemoveAttribute() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Elements("Item") 8                         where ele.Value.Equals("Item1") 9                         select ele).FirstOrDefault().Attribute("v1");10             if (item != null)11             {12                 item.Remove();13             }14             xele.Save(Path);15         }

 

 

我们首先查询出指定的节点,然后指定某个属性,最后调用XAttributeRemove方法既可。

 

结果如下:

1 
2
3
4
5
Item1
6
7
8
Item3
9

 

 

7.删除指定节点

既然上面已经可以删除属性,自然也少不了删除属性。

 

代码如下所示:

1         ///  2         /// 删除指定节点 3         ///  4         public static void RemoveElement() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Elements("Item") 8                         where ele.Value.Equals("Item1") 9                         select ele).FirstOrDefault();10             if (item != null)11             {12                 item.Remove();13             }14             xele.Save(Path);15         }

 

 

依然是调用同样的方法。

 

结果如下:

1 
2
3
4
5
6
7
Item3
8

 

 

三、按节点关系查询

上面的查询都是通过相关的条件进行查询,但是我们有时仅仅只需要通过之间的关系即可,这样反而可以避免很多的代码,当然稍加探索可以发现其实XElement都提供给我们了。

我们依然要新建一个StructureXml类,并在其中新建一个属性。

 

如下所示:

1         public static String Path2         {3             get4             {5                 String path = String.Format("{0}\\test2.xml", Environment.CurrentDirectory);6                 return path;7             }8         }

 

 

同时在该文件夹下新建一个test2.xml并写入如下内容:

1 
2
3
4
5 1 6
7
8
9 sss10
11
12
13 214
15
16

 

 

1.显示指定节点的所有父节点

通过上面的xml文件,我们清晰的看出xml是具有结构性的,彼此之间都存在关系,而现在我们需要显示某个节点的父级节点的名称。

 

代码如下所示:

1         ///  2         /// 显示指定节点的所有父节点 3         ///  4         public static void ShowAllParentEle() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Descendants("Child") 8                         select ele).FirstOrDefault(); 9             if (item != null)10             {11                 foreach (var sub in item.Ancestors())12                 {13                     Console.WriteLine(sub.Name);14                 }15                 Console.WriteLine("----------------");16                 foreach (var sub in item.AncestorsAndSelf())17                 {18                     Console.WriteLine(sub.Name);19                 }20                 Console.ReadKey();21             }22         }

 

 

其中我们通过Descendants获取最底的节点,然后使用Ancestors获取所有的父级节点,而AncestorsAndSelf则表示包含本身。

 

最终结果如下所示:

 

我们从图中看出,分割线前显示的是不包含本身的,而下面是包含本身的。

 

2.显示指定节点的所有子节点

我们不仅仅可以输出一个节点的所有父级节点,同样也可以输出一个节点的所有子节点。

 

代码如下所示:

1         ///  2         /// 显示指定节点的所有子节点 3         ///  4         public static void ShowAllChildEle() 5         { 6             XElement xele = XElement.Load(Path); 7             foreach (var sub in xele.Descendants()) 8             { 9                 Console.WriteLine(sub.Name);10             }11             Console.WriteLine("-----------------");12             foreach (var sub in xele.DescendantsAndSelf())13             {14                 Console.WriteLine(sub.Name);15             }16             Console.ReadKey();17         }

 

 

这里我们依然是分成输出子级节点以及包含自己的。

 

结果如下所示:

 

3.显示同级节点之前的节点

既然有了父子关系,当然也少不了同级关系,首先我们先显示同级节点之前的节点。

 

代码如下所示:

1         ///  2         /// 显示同级节点之前的节点 3         ///  4         public static void ShowPrevEle() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Descendants("SubItem") 8                         select ele).FirstOrDefault(); 9             if (item != null)10             {11                 foreach (var sub in item.ElementsBeforeSelf())12                 {13                     Console.WriteLine(sub.Name);14                 }15             }16             Console.ReadKey();17         }

 

 

这里我们看到我们通过ElementsBeforeSelf获取该节点之前的同级节点,当然我们还可以传入参数作为限制条件。这里我们通过查询获取了SubItem这个节点,并显示该节点之前的同级节点。

 

最终结果如下:

 

4.显示同级节点后面的节点

作为上面的补充。

 

代码如下所示:

1        ///  2         /// 显示同级节点后面的节点 3         ///  4         public static void ShowNextEle() 5         { 6             XElement xele = XElement.Load(Path); 7             var item = (from ele in xele.Descendants("SubItem") 8                         select ele).FirstOrDefault(); 9             if (item != null)10             {11                 foreach (var sub in item.ElementsAfterSelf())12                 {13                     Console.WriteLine(sub.Name);14                 }15             }16             Console.ReadKey();17         }

 

 

最终结果如下所示:

 

四、监听xml事件

你可能会疑惑xml为什么还要监听,其实这样是有意义的,比如你要根据某个节点的值作为依赖,那么你就要监听这个节点,如果这个节点发生改变的时候,

你才可以及时的作出反应。但是xml的事件监听有一个特点,跟浏览器中的DOM事件类似,监听父节点同样也可以监听的到它的子节点的事件。下面我们

通过一个简单的实例来说明。

 

实例代码如下:

1     public static class EventXml 2     { 3         public static void BindChangeing() 4         { 5             XElement xele = new XElement("Root"); 6             xele.Changing += xele_Changing; 7             xele.Changed += xele_Changed; 8             xele.Add(new XElement("Item", "123")); 9             var item = xele.Element("Item");10             item.ReplaceWith(new XElement("Item", "2"));11             item = xele.Element("Item");12             item.Remove();13             Console.ReadKey();14         }15 16         static void xele_Changed(object sender, XObjectChangeEventArgs e)17         {18             XElement ele = sender as XElement;19             Console.WriteLine(String.Format("已完成 {0}-{1}", ele.Name, e.ObjectChange));20         }21 22         static void xele_Changing(object sender, XObjectChangeEventArgs e)23         {24             XElement ele = sender as XElement;25             Console.WriteLine(String.Format("正在进行中 {0}-{1}", ele.Name, e.ObjectChange));26         }27 }

 

 

其中的关键就是ChangingChanged事件,其次就是在事件中判断事件的来源。

 

最终结果如下所示:

 

五、处理xml流

在实际的商业化的开发中,xml不可能仅仅保存这么点数据。有可能保存着非常多的数据。但是我们还是按照以往的方式,就会将xml全部读取进内存。

这样会占据很多内存,影响系统的性能,针对这种情况我们需要使用流的方式去处理xml,因为流会按照我们的顺序读取部分xml进内存,并不会将所

xml都读取进内存。

 

Xml文件内容如下所示:

1 
2
3
1
4
1
5
1
6
A
7
1
8
B
9

 

 

代码如下所示:

1     public static class ReadXmlStream 2     { 3         public static String Path 4         { 5             get 6             { 7                 String path = String.Format("{0}\\test3.xml", Environment.CurrentDirectory); 8                 return path; 9             }10         }11 12         /// 13         /// 流式处理XML14         /// 15         public static void ReadXml()16         {17             XmlReader reader = XmlReader.Create(Path);18             while (reader.Read())19             {20                 if (reader.NodeType == XmlNodeType.Element && reader.Name.Equals("Item"))21                 {22                     XElement ele = XElement.ReadFrom(reader) as XElement;23                     Console.WriteLine(ele.Value.Trim());24                 }25             }26             Console.ReadKey();27         }28 }

 

 

这里我们通过XmlReaderCreate静态方法打开xml文件,并通过Read一个节点的进行读取,并判断该节点的类型。

 

最终结果如下:

 

转载地址:http://hhgho.baihongyu.com/

你可能感兴趣的文章
基于lnmp的Discuz论坛
查看>>
Xcode中的 编译过程以及编译器
查看>>
OSV配合windows 2008 r2 NPS 搭建802.1X认证环境
查看>>
01-Swift基础语法
查看>>
【MySQL】无法进入mysql connections问题
查看>>
再说TCP神奇的40ms
查看>>
eclipse hibernate配置文件(*.hbm.xml)加上自动提示功能
查看>>
extjs 枚举类型
查看>>
五、Hotspot中高效的垃圾回收算法实现
查看>>
发送邮件常见的错误和解决方法
查看>>
机器学习服务器 PredictionIO 脱颖而出
查看>>
mysql不能连接远程mysql服务器
查看>>
Windows 8.1 重复数据删除——概念(一)
查看>>
iptables防火墙高级应用
查看>>
python运维-Socket网络编程
查看>>
yum管理包流程_学习笔记
查看>>
DeltaGrad领跑智能化交易领域 预见收益颠覆基金行业
查看>>
nginx keepalived tomcat实现的高可用
查看>>
Https能避免流量劫持吗?
查看>>
oracle教程之oracle 删除表空间
查看>>